net.di2e.ecdr.source.rest.CDROpenSearchSource.java Source code

Java tutorial

Introduction

Here is the source code for net.di2e.ecdr.source.rest.CDROpenSearchSource.java

Source

/**
 * Copyright (C) 2014 Cohesive Integrations, LLC (info@cohesiveintegrations.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.di2e.ecdr.source.rest;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.activation.MimeType;
import javax.activation.MimeTypeParseException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
import org.codice.ddf.security.common.jaxrs.RestSecurity;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ddf.catalog.data.ContentType;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.Result;
import ddf.catalog.data.impl.ResultImpl;
import ddf.catalog.filter.FilterAdapter;
import ddf.catalog.operation.Query;
import ddf.catalog.operation.QueryRequest;
import ddf.catalog.operation.ResourceResponse;
import ddf.catalog.operation.SourceResponse;
import ddf.catalog.operation.impl.ResourceRequestByProductUri;
import ddf.catalog.operation.impl.ResourceResponseImpl;
import ddf.catalog.operation.impl.SourceResponseImpl;
import ddf.catalog.resource.ResourceNotFoundException;
import ddf.catalog.resource.ResourceNotSupportedException;
import ddf.catalog.resource.impl.ResourceImpl;
import ddf.catalog.service.ConfiguredService;
import ddf.catalog.source.ConnectedSource;
import ddf.catalog.source.FederatedSource;
import ddf.catalog.source.SourceMonitor;
import ddf.catalog.source.UnsupportedQueryException;
import ddf.security.SecurityConstants;
import ddf.security.Subject;
import net.di2e.ecdr.api.cache.CacheManager;
import net.di2e.ecdr.api.queryresponse.SearchResponseTransformer;
import net.di2e.ecdr.commons.constants.SearchConstants;
import net.di2e.ecdr.commons.filter.StrictFilterDelegate;
import net.di2e.ecdr.search.transform.atom.response.AtomResponseTransformer;

public class CDROpenSearchSource extends CDRSourceConfiguration
        implements FederatedSource, ConnectedSource, ConfiguredService {

    private static final Logger LOGGER = LoggerFactory.getLogger(CDROpenSearchSource.class);
    private static final String HEADER_ACCEPT_RANGES = "Accept-Ranges";
    private static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
    private static final String HEADER_RANGE = "Range";
    private static final String BYTES_TO_SKIP = "BytesToSkip";
    private static final String BYTES_SKIPPED_RESPONSE = "BytesSkipped";
    private static final String BYTES = "bytes";
    private static final String BYTES_EQUAL = "bytes=";

    private SourceMonitor sourceMonitor = null;
    private FilterAdapter filterAdapter = null;

    private Date lastAvailableCheckDate = null;
    private boolean isCurrentlyAvailable = false;
    private String localId = null;

    private WebClient cdrRestClient = null;
    private WebClient cdrAvailabilityCheckClient = null;

    private String configurationPid = null;

    public CDROpenSearchSource(FilterAdapter adapter, CacheManager<Metacard> manager) {
        super(manager);
        this.filterAdapter = adapter;
    }

    @Override
    public SourceResponse query(QueryRequest queryRequest) throws UnsupportedQueryException {
        try {
            Query query = queryRequest.getQuery();
            SourceResponse sourceResponse;
            // ECDR-72 Add in default radius
            Map<String, String> filterParameters = filterAdapter.adapt(query,
                    new StrictFilterDelegate(false, getSupportedGeoOption(), getPropertyMap(), getDateTypeMap()));
            String id = filterParameters.get(SearchConstants.UID_PARAMETER);
            // check if this is an id-only query
            if (StringUtils.isBlank(id)) {
                // non-id query, perform normal search
                sourceResponse = doQuery(filterParameters, queryRequest);
            } else {
                // id-only query, check if remote source supports it
                if (supportsQueryById()) {
                    sourceResponse = doQuery(filterParameters, queryRequest);
                } else {
                    sourceResponse = lookupById(queryRequest, id);
                }
            }
            return sourceResponse;

        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new UnsupportedQueryException(
                    "Could not complete query to site [" + localId + "] due to: " + e.getMessage(), e);
        }
    }

    protected SourceResponse doQuery(Map<String, String> filterParameters, QueryRequest queryRequest)
            throws UnsupportedQueryException {
        SourceResponse sourceResponse;
        SearchResponseTransformer transformer = lookupSearchResponseTransformer();

        setSecurityCredentials(cdrRestClient, queryRequest.getProperties());
        filterParameters.putAll(getInitialFilterParameters(queryRequest));
        setURLQueryString(filterParameters);
        setHttpHeaders(filterParameters, cdrRestClient);
        LOGGER.debug("Executing http GET query to source [{}] with url [{}]", localId,
                cdrRestClient.getCurrentURI().toString());
        // TLSUtil.setTLSOptions( cdrRestClient );
        Response response = cdrRestClient.get();
        LOGGER.debug("Query to source [{}] returned http status code [{}] and media type [{}]", localId,
                response.getStatus(), response.getMediaType());

        if (response.getStatus() == Status.OK.getStatusCode()) {
            // Be sure to pass in the getId() instead of the localId so Connected sources populate the Metacard with the
            // right Id
            sourceResponse = transformer.processSearchResponse((InputStream) response.getEntity(), queryRequest,
                    getId());
            if (!supportsQueryById()) {
                sourceResponse = cacheResults(sourceResponse);
            }
        } else {
            Object entity = response.getEntity();
            if (entity != null) {
                try {
                    LOGGER.warn("Error status code received [{}] when querying site [{}]:{}[{}]",
                            response.getStatus(), localId, System.lineSeparator(),
                            IOUtils.toString((InputStream) entity));
                } catch (IOException e) {
                    LOGGER.warn("Error status code received [{}] when querying site [{}]", response.getStatus(),
                            localId);
                }
            } else {
                LOGGER.warn("Error status code received [{}] when querying site [{}]", response.getStatus(),
                        localId);
            }
            throw new UnsupportedQueryException(
                    "Query to remote source returned http status code " + response.getStatus());
        }
        return sourceResponse;
    }

    @Override
    public boolean isAvailable() {
        LOGGER.debug(
                "isAvailable method called on CDR Rest Source named [{}], determining whether to check availability or pull from cache",
                localId);
        if (getPingMethod() != null && !PingMethod.NONE.equals(getPingMethod())
                && cdrAvailabilityCheckClient != null) {
            if (!isCurrentlyAvailable || (lastAvailableCheckDate.getTime() < System.currentTimeMillis()
                    - getAvailableCheckCacheTime())) {
                LOGGER.debug(
                        "Checking availability on CDR Rest Source named [{}] in real time by calling endpoint [{}]",
                        localId, cdrAvailabilityCheckClient.getBaseURI());
                try {

                    Response response = PingMethod.HEAD.equals(getPingMethod()) ? getPingClient().head()
                            : getPingClient().get();
                    if (response.getStatus() == Status.OK.getStatusCode()
                            || response.getStatus() == Status.ACCEPTED.getStatusCode()) {
                        isCurrentlyAvailable = true;
                        lastAvailableCheckDate = new Date();
                    } else {
                        isCurrentlyAvailable = false;
                    }
                } catch (RuntimeException e) {
                    LOGGER.warn("CDR Rest Source named [" + localId
                            + "] encountered an unexpected error while executing HTTP Head at URL ["
                            + cdrAvailabilityCheckClient.getBaseURI() + "]:" + e.getMessage());
                    LOGGER.debug("Exception while trying to check avilability of site {}", localId, e);
                    isCurrentlyAvailable = false;
                }

            } else {
                LOGGER.debug(
                        "Pulling availability of CDR Rest Federated Source named [{}] from cache, isAvailable=[{}]",
                        localId, isCurrentlyAvailable);
            }
            //if ( CollectionUtils.isNotEmpty( sourceMonitors ) ) {
            if (sourceMonitor != null) {
                //  for( SourceMonitor sourceMonitor : sourceMonitors ){
                if (isCurrentlyAvailable) {
                    sourceMonitor.setAvailable();
                } else {
                    sourceMonitor.setUnavailable();
                }
                //  }
            }
        } else {
            LOGGER.debug(
                    "HTTP Ping is set to false so not checking the sites availability, just setting to available");
            isCurrentlyAvailable = true;
            //if ( CollectionUtils.isNotEmpty( sourceMonitors ) ) {
            if (sourceMonitor != null) {
                //   for( SourceMonitor sourceMonitor : sourceMonitors ){
                sourceMonitor.setAvailable();
                //   }
            }
        }
        return isCurrentlyAvailable;
    }

    @Override
    public void setId(String id) {
        LOGGER.debug("ConfigUpdate: Updating site name to [{}] by setId method", id);
        super.setId(id);
        localId = id;
    }

    @Override
    public void setShortname(String id) {
        LOGGER.debug("ConfigUpdate: Updating site name to [{}] by setShortname method", id);
        super.setId(id);
        localId = id;
    }

    @Override
    public boolean isAvailable(SourceMonitor callback) {
        sourceMonitor = callback;
        return isAvailable();
    }

    @Override
    public Set<ContentType> getContentTypes() {
        return Collections.emptySet();
    }

    @Override
    public Set<String> getOptions(Metacard paramMetacard) {
        return Collections.emptySet();
    }

    @Override
    public Set<String> getSupportedSchemes() {
        return Collections.emptySet();
    }

    @Override
    public ResourceResponse retrieveResource(URI uri, Map<String, Serializable> requestProperties)
            throws ResourceNotFoundException, ResourceNotSupportedException, IOException {
        LOGGER.debug("Retrieving Resource from remote CDR Source named [{}] using URI [{}]", localId, uri);

        // Check to see if the resource-uri value was passed through which is
        // the original metacard uri which
        // can be different from what was returned or used by the client
        Serializable resourceUriProperty = requestProperties.get(Metacard.RESOURCE_URI);
        if (resourceUriProperty != null && resourceUriProperty instanceof URI) {
            URI resourceUri = (URI) resourceUriProperty;
            if (!resourceUri.equals(uri)) {
                LOGGER.debug(
                        "Overriding the passed in resourceUri [{}] with the value found in the request properties [{}]",
                        uri, resourceUri);
                uri = resourceUri;
            }

        } else if (uri != null) {
            String scheme = uri.getScheme();
            if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) {
                uri = getURIFromMetacard(uri);
            }
        }

        ResourceResponse resourceResponse = null;
        if (uri != null) {
            LOGGER.debug("Retrieving the remote resource using the uri [{}]", uri);
            WebClient retrieveWebClient = WebClient.create(uri);
            WebClient.getConfig(retrieveWebClient).getHttpConduit();
            TLSUtil.setTLSOptions(retrieveWebClient, getDisableCNCheck());
            resourceResponse = doRetrieval(retrieveWebClient, requestProperties);
        }

        if (resourceResponse == null) {
            LOGGER.warn("Could not retrieve resource from CDR Source named [{}] using uri [{}]", localId, uri);
            throw new ResourceNotFoundException(
                    "Could not retrieve resource from source [" + localId + "] and uri [" + uri + "]");
        }
        return resourceResponse;
    }

    protected ResourceResponse doRetrieval(WebClient retrieveWebClient, Map<String, Serializable> requestProperties)
            throws ResourceNotFoundException, IOException {
        ResourceResponse resourceResponse = null;
        setSecurityCredentials(retrieveWebClient, requestProperties);
        URI uri = retrieveWebClient.getCurrentURI();
        try {

            Long bytesToSkip = null;
            // If a bytesToSkip property is present add range header
            if (requestProperties != null && requestProperties.containsKey(BYTES_TO_SKIP)) {
                bytesToSkip = (Long) requestProperties.get(BYTES_TO_SKIP);
                if (bytesToSkip != null) {
                    LOGGER.debug(
                            "Setting Range header on retrieve request from remote CDR Source [{}] with bytes to skip [{}]",
                            localId, bytesToSkip);
                    // This creates a Range header in the following manner if
                    // 100 bytes were to be skipped. The end - means its open
                    // ended
                    // Range: bytes=100-
                    retrieveWebClient.header(HEADER_RANGE, BYTES_EQUAL + bytesToSkip + "-");
                }
            }

            Response clientResponse = retrieveWebClient.get();

            MediaType mediaType = clientResponse.getMediaType();
            MimeType mimeType = null;
            try {
                mimeType = (mediaType == null) ? new MimeType("application/octet-stream")
                        : new MimeType(mediaType.toString());
                LOGGER.debug(
                        "Creating mime type from CDR Source named [{}] using uri [{}] with value [{}] defaulting to [{}]",
                        localId, uri, mediaType);
            } catch (MimeTypeParseException e) {
                try {
                    mimeType = new MimeType("application/octet-stream");
                    LOGGER.warn(
                            "Creating mime type from CDR Source named [{}] using uri [{}] with value [{}] defaulting to [{}]",
                            localId, uri, "application/octet-stream");
                } catch (MimeTypeParseException e1) {
                    LOGGER.error("Could not create MIMEType for resource being retrieved", e1);
                }

            }

            String dispositionString = clientResponse.getHeaderString(HEADER_CONTENT_DISPOSITION);

            String fileName = null;
            if (dispositionString != null) {
                ContentDisposition contentDisposition = new ContentDisposition(dispositionString);
                fileName = contentDisposition.getParameter("filename");
                if (fileName == null) {
                    fileName = contentDisposition.getParameter("\"filename\"");
                }
                if (fileName == null) {
                    // ECDR-74 use MIMEType parser to get the file extension in
                    fileName = getId() + "-" + System.currentTimeMillis();
                }
            } else {
                // ECDR-74 use MIMEType parser to get the file extension in this
                // case
                fileName = getId() + "-" + System.currentTimeMillis();
            }

            InputStream binaryStream = (InputStream) clientResponse.getEntity();
            if (binaryStream != null) {
                Map<String, Serializable> responseProperties = new HashMap<String, Serializable>();
                if (bytesToSkip != null) {
                    // Since we sent a range header an accept-ranges header
                    // should be returned if the
                    // remote endpoint support it. If is not present, the
                    // inputStream hasn't skipped ahead
                    // by the given number of bytes, so we need to take care of
                    // it here.
                    String rangeHeader = clientResponse.getHeaderString(HEADER_ACCEPT_RANGES);
                    if (rangeHeader == null || !rangeHeader.equals(BYTES)) {
                        LOGGER.debug(
                                "Skipping {} bytes in CDR Remote Source because endpoint didn't support Range Headers",
                                bytesToSkip);
                        try {
                            // the Java inputStream.skip() method is not
                            // guaranteed to skip all the bytes so we use a
                            // utility method that is
                            IOUtils.skipFully(binaryStream, bytesToSkip);
                        } catch (EOFException e) {
                            LOGGER.warn(
                                    "Skipping the requested number of bytes [{}] for URI [{}] resulted in an End of File, so re-retrieving the complete file without skipping bytes: {}",
                                    bytesToSkip, uri, e.getMessage());
                            try {
                                binaryStream.close();
                            } catch (IOException e1) {
                                LOGGER.debug("Error encountered while closing inputstream");
                            }
                            return doRetrieval(retrieveWebClient, null);
                        }
                    } else if (rangeHeader != null && rangeHeader.equals(BYTES)) {
                        LOGGER.debug(
                                "CDR Remote source supports Range Headers, only retrieving part of file that has not been downloaded yet.");
                        responseProperties.put(BYTES_SKIPPED_RESPONSE, Boolean.TRUE);
                    }
                }
                resourceResponse = new ResourceResponseImpl(new ResourceRequestByProductUri(uri, requestProperties),
                        responseProperties, new ResourceImpl(binaryStream, mimeType, fileName));
            }
        } catch (RuntimeException e) {
            LOGGER.warn(
                    "Expected exception encountered when trying to retrieve resource with URI [{}] from source [{}]",
                    uri, localId);
        }
        return resourceResponse;
    }

    protected SearchResponseTransformer lookupSearchResponseTransformer() throws UnsupportedQueryException {
        SearchResponseTransformer transformer;
        if (StringUtils.isBlank(getResponseTransformerName())) {
            transformer = new AtomResponseTransformer(getAtomResponseTransformerConfig());
            LOGGER.debug("Using the default Atom Response Transformer to transform response from site [{}]",
                    localId);
        } else {
            transformer = getSearchResponseTransformer(getResponseTransformerName());
        }
        if (transformer == null) {
            throw new UnsupportedQueryException("The query was not executed on the source " + localId
                    + " because the response transformer was not a valid value [" + getResponseTransformerName()
                    + "]. Please check the source configuration value for 'Response Transformer Override'");
        }
        return transformer;
    }

    protected void setURLQueryString(Map<String, String> filterParameters) {
        cdrRestClient.resetQuery();
        for (Entry<String, String> entry : filterParameters.entrySet()) {
            String parameterName = getParameterMap().get(entry.getKey());
            if (StringUtils.isNotBlank(parameterName)) {
                cdrRestClient.replaceQueryParam(parameterName, entry.getValue());
            }
        }

        Map<String, String> hardcodedQueryParams = getHardcodedQueryParameters();
        for (Entry<String, String> entry : hardcodedQueryParams.entrySet()) {
            cdrRestClient.replaceQueryParam(entry.getKey(), entry.getValue());
        }
    }

    protected Map<String, String> getInitialFilterParameters(QueryRequest request) {
        Map<String, String> filterParameters = new HashMap<String, String>();
        Map<String, Serializable> queryRequestProps = request.getProperties();

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(
                    "CDR REST Source received Query: " + ToStringBuilder.reflectionToString(request.getQuery()));
        }

        // include format parameter
        String format = (String) queryRequestProps.get(SearchConstants.FORMAT_PARAMETER);
        if (StringUtils.isNotBlank(format)) {
            filterParameters.put(SearchConstants.FORMAT_PARAMETER, format);
        }

        // Strict Mode
        Boolean strictMode = (Boolean) queryRequestProps.get(SearchConstants.STRICTMODE_PARAMETER);
        if (strictMode != null) {
            filterParameters.put(SearchConstants.STRICTMODE_PARAMETER, String.valueOf(strictMode));
        }

        Query query = request.getQuery();

        // Include timeout
        long timeout = query.getTimeoutMillis();
        if (timeout > 1000) {
            filterParameters.put(SearchConstants.TIMEOUT_PARAMETER, String.valueOf(timeout));
        }

        if (getParameterMap().containsKey(SearchConstants.COUNT_PARAMETER)) {
            int pageSize = query.getPageSize();
            filterParameters.put(SearchConstants.COUNT_PARAMETER,
                    getMaxResultsCount() > 0 && pageSize > getMaxResultsCount()
                            ? String.valueOf(getMaxResultsCount())
                            : String.valueOf(pageSize));
        }

        if (getParameterMap().containsKey(SearchConstants.STARTINDEX_PARAMETER)) {
            int startIndex = query.getStartIndex();
            filterParameters.put(SearchConstants.STARTINDEX_PARAMETER, String.valueOf(
                    getAtomResponseTransformerConfig().isZeroBasedStartIndex() ? startIndex - 1 : startIndex));
        }

        String sortOrderString = getSortOrderString(query.getSortBy());
        LOGGER.trace("Getting sort order for query [{}]", sortOrderString);
        if (sortOrderString != null) {
            filterParameters.put(SearchConstants.SORTKEYS_PARAMETER, sortOrderString);
        }

        for (Entry<String, Serializable> entry : queryRequestProps.entrySet()) {
            String key = entry.getKey();
            if (getParameterMap().containsKey(key) || getHttpHeaderList().contains(key)) {
                String value = (String) entry.getValue();
                if (StringUtils.isNotBlank(value)) {
                    filterParameters.put(key, value);
                }
            }
        }

        LOGGER.trace("Filter Parameters being evaluated for inclusion in outgoing query {} which were parsed from",
                filterParameters);
        return filterParameters;
    }

    protected URI getURIFromMetacard(URI uri) {
        URI returnUri = null;
        Map<String, String> uriMap = new HashMap<String, String>(3);
        uriMap.put(Metacard.RESOURCE_URI, uri.toString());
        setURLQueryString(uriMap);
        Response response = cdrRestClient.get();
        AtomResponseTransformer transformer = new AtomResponseTransformer(getAtomResponseTransformerConfig());
        SourceResponse sourceResponse = transformer.processSearchResponse((InputStream) response.getEntity(), null,
                getId());
        List<Result> results = sourceResponse.getResults();
        if (!results.isEmpty()) {
            returnUri = results.get(0).getMetacard().getResourceURI();
        }
        return returnUri;
    }

    public SourceResponse cacheResults(SourceResponse sourceResponse) {
        for (Result result : sourceResponse.getResults()) {
            Metacard metacard = result.getMetacard();
            getMetacardCache().put(metacard.getId(), metacard);
        }
        return sourceResponse;
    }

    private void setSecurityCredentials(WebClient client, Map<String, Serializable> requestProperties) {
        if (isSendSecurityCookie()) {
            if (requestProperties.containsKey(SecurityConstants.SECURITY_SUBJECT)) {
                Serializable property = requestProperties.get(SecurityConstants.SECURITY_SUBJECT);
                if (property instanceof Subject) {
                    Subject subject = (Subject) property;
                    RestSecurity.setUnsecuredSubjectOnClient(subject, client);
                }
            }
        }
    }

    protected SearchResponseTransformer getSearchResponseTransformer(String id) {
        SearchResponseTransformer transformer = null;
        Bundle bundle = FrameworkUtil.getBundle(this.getClass());
        if (bundle != null) {
            BundleContext context = bundle.getBundleContext();
            Collection<ServiceReference<SearchResponseTransformer>> transformers;
            try {
                transformers = context.getServiceReferences(SearchResponseTransformer.class, "(id=" + id + ")");
                int size = transformers.size();
                if (size > 0) {
                    transformer = context.getService(transformers.iterator().next());
                    if (size > 1) {
                        LOGGER.debug(
                                "Multiple [{}] InputTransformers were returned when looking up InputTransformer with id [{}], using the first one {}",
                                size, id, transformer.getClass().getName());
                    }
                }
            } catch (InvalidSyntaxException e) {
                LOGGER.warn("Could not lookup input transformer with id [{}]", id, e.getMessage());
            }
        }
        return transformer;
    }

    protected SourceResponse lookupById(QueryRequest queryRequest, String id) throws UnsupportedQueryException {
        SourceResponse sourceResponse = null;
        LOGGER.debug("Checking cache for Result with id [{}].", id);
        Metacard metacard = getMetacardCache().get(id);
        if (metacard != null) {
            metacard.setSourceId(getId());
            LOGGER.debug("Cache hit found for id [{}], returning response", id);
            sourceResponse = new SourceResponseImpl(queryRequest, Arrays.asList((Result) new ResultImpl(metacard)),
                    1L);
        } else {
            LOGGER.debug("Could not find result id [{}] in cache", id);
            throw new UnsupportedQueryException(
                    "Queries for parameter uid are not supported by source [" + localId + "]");
        }
        return sourceResponse;
    }

    protected void setHttpHeaders(Map<String, String> filterParameters, WebClient client) {
        Map<String, String> hardcodedHeaders = getHardcodedHttpHeaders();
        if (MapUtils.isNotEmpty(hardcodedHeaders)) {
            for (Entry<String, String> entry : hardcodedHeaders.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                client.header(key, value);
                LOGGER.trace("Adding the following HTTP Header to outgoing request [{}]=[{}]", key, value);
            }
        }

        List<String> headers = getHttpHeaderList();
        if (CollectionUtils.isNotEmpty(headers)) {
            for (String header : headers) {
                if (filterParameters.containsKey(header)) {
                    String value = filterParameters.get(header);
                    client.header(header, value);
                    LOGGER.trace("Adding the following HTTP Header to outgoing request [{}]=[{}]", header, value);
                }
            }
        }
    }

    protected boolean supportsQueryById() {
        return getParameterMap().containsKey(SearchConstants.UID_PARAMETER);
    }

    @Override
    protected WebClient getRestClient() {
        return cdrRestClient;
    }

    @Override
    protected void setRestClient(WebClient webClient) {
        cdrRestClient = webClient;
    }

    @Override
    protected WebClient getPingClient() {
        return cdrAvailabilityCheckClient;
    }

    @Override
    protected void setPingClient(WebClient webClient) {
        cdrAvailabilityCheckClient = webClient;
    }

    @Override
    public String getConfigurationPid() {
        return configurationPid;
    }

    @Override
    public void setConfigurationPid(String pid) {
        configurationPid = pid;
    }

}