net.di2e.ecdr.search.transform.atom.AbstractAtomTransformer.java Source code

Java tutorial

Introduction

Here is the source code for net.di2e.ecdr.search.transform.atom.AbstractAtomTransformer.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.search.transform.atom;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.activation.MimeType;
import javax.activation.MimeTypeParseException;
import javax.xml.namespace.QName;

import net.di2e.ecdr.api.security.SecurityConfiguration;
import net.di2e.ecdr.api.security.SecurityData;
import net.di2e.ecdr.api.security.SecurityMarkingHandler;
import net.di2e.ecdr.commons.CDRMetacard;
import net.di2e.ecdr.commons.constants.SearchConstants;
import net.di2e.ecdr.commons.constants.SecurityConstants;
import net.di2e.ecdr.search.transform.atom.constants.AtomResponseConstants;
import net.di2e.ecdr.search.transform.atom.geo.GeoHelper;
import net.di2e.ecdr.search.transform.atom.security.impl.ConfigurationSecurityMarkingHandler;
import net.di2e.ecdr.search.transform.atom.security.impl.MetacardSecurityMarkingHandler;
import net.di2e.ecdr.search.transform.atom.security.impl.XmlMetadataSecurityMarkingHandler;
import net.di2e.ecdr.search.transform.geo.formatter.CompositeGeometry;

import org.apache.abdera.Abdera;
import org.apache.abdera.ext.geo.Position;
import org.apache.abdera.ext.opensearch.OpenSearchConstants;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.ExtensibleElement;
import org.apache.abdera.model.Feed;
import org.apache.abdera.model.Link;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.util.URIUtil;
import org.apache.commons.lang.StringUtils;
import org.codice.ddf.configuration.SystemInfo;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;

import ddf.action.Action;
import ddf.action.ActionProvider;
import ddf.catalog.data.BinaryContent;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.Result;
import ddf.catalog.data.impl.BinaryContentImpl;
import ddf.catalog.operation.ProcessingDetails;
import ddf.catalog.operation.QueryRequest;
import ddf.catalog.operation.QueryResponse;
import ddf.catalog.operation.SourceResponse;
import ddf.catalog.transform.CatalogTransformerException;
import ddf.catalog.transform.MetacardTransformer;
import ddf.catalog.transform.QueryResponseTransformer;

public abstract class AbstractAtomTransformer implements MetacardTransformer, QueryResponseTransformer {

    protected static final String CDR_ATOM_TRANSFORMER_ID = "cdr-atom";

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAtomTransformer.class);
    private static final DateTimeFormatter DATE_FORMATTER = ISODateTimeFormat.dateTime();

    private ActionProvider viewMetacardActionProvider = null;
    private ActionProvider resourceActionProvider = null;
    private ActionProvider thumbnailActionProvider = null;
    private ActionProvider metadataActionProvider = null;
    private List<SecurityConfiguration> securityConfigurations = null;
    private MimeType thumbnailMimeType = null;
    private MimeType viewMimeType = null;

    private boolean isTransform41 = false;
    private boolean isTransform50 = false;

    private boolean defaultToUseGMLEncoding = true;

    List<SecurityMarkingHandler> securityHandlers = null;

    public AbstractAtomTransformer(ActionProvider viewMetacard, ActionProvider metadataProvider,
            ActionProvider resourceProvider, ActionProvider thumbnailProvider, MimeType thumbnailMime,
            MimeType viewMime, List<SecurityConfiguration> securityConfigs) {
        if (viewMime == null || thumbnailMime == null) {
            throw new IllegalArgumentException("MimeType parameters to constructor cannot be null");
        }
        this.viewMetacardActionProvider = viewMetacard;
        this.metadataActionProvider = metadataProvider;
        this.resourceActionProvider = resourceProvider;
        this.thumbnailActionProvider = thumbnailProvider;
        this.thumbnailMimeType = thumbnailMime;
        this.viewMimeType = viewMime;
        this.securityConfigurations = securityConfigs;

        securityHandlers = new ArrayList<SecurityMarkingHandler>();
        securityHandlers.add(new MetacardSecurityMarkingHandler());
        securityHandlers.add(new XmlMetadataSecurityMarkingHandler());
        SecurityConfiguration metacardDefault = getConfigurationFromFormat("metacard-default");
        if (metacardDefault != null) {
            securityHandlers.add(new ConfigurationSecurityMarkingHandler(metacardDefault));
        }
    }

    public abstract void addFeedElements(Feed feed, SourceResponse response, Map<String, Serializable> properties);

    public abstract void addEntryElements(Entry entry, CDRMetacard metacard, Map<String, Serializable> properties);

    /**
     * Specifies if GML encoding should be used for location data.
     *
     * @param shouldUseGMLEncoding
     *            true (default) will return locations as GeoRSS-GML; false will return locations as GeoRSS-Simple
     */
    public void setUseGMLEncoding(boolean shouldUseGMLEncoding) {
        defaultToUseGMLEncoding = shouldUseGMLEncoding;
    }

    @Override
    public BinaryContent transform(SourceResponse response, Map<String, Serializable> properties)
            throws CatalogTransformerException {
        if (properties == null) {
            properties = new HashMap<String, Serializable>();
        }

        ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
        Feed feed = null;
        // The Adbera.getInstance.newFeed() spins up a new thread so must do
        // this
        try {
            Thread.currentThread().setContextClassLoader(AbstractAtomTransformer.class.getClassLoader());
            feed = Abdera.getInstance().newFeed();
        } finally {
            Thread.currentThread().setContextClassLoader(currentClassLoader);
        }

        feed.declareNS(AtomResponseConstants.CDRB_NAMESPACE, AtomResponseConstants.CDRB_NAMESPACE_PREFIX);
        feed.declareNS(AtomResponseConstants.CDRS_EXT_NAMESPACE, AtomResponseConstants.CDRS_EXT_NAMESPACE_PREFIX);

        feed.newId();
        setFeedTitle(feed, properties);
        feed.setUpdated(new Date());

        List<Result> results = response.getResults();
        QueryRequest queryRequest = response.getRequest();

        feed.addExtension(OpenSearchConstants.ITEMS_PER_PAGE).setText(String.valueOf(results.size()));
        feed.addExtension(OpenSearchConstants.START_INDEX)
                .setText(String.valueOf(queryRequest.getQuery().getStartIndex()));
        feed.addExtension(OpenSearchConstants.TOTAL_RESULTS).setText(String.valueOf(response.getHits()));

        feed.setGenerator(null, SystemInfo.getVersion(), SystemInfo.getSiteName());
        feed.addAuthor(SystemInfo.getOrganization(), SystemInfo.getSiteContatct(), null);

        addLinksToFeed(feed, properties);

        if (!isFalse((Boolean) properties.get(SearchConstants.STATUS_PARAMETER))) {
            addStatus(response, feed, results, properties);
        }

        addFeedElements(feed, response, properties);

        Entry entry = null;
        for (Result result : results) {
            String id = null;
            try {
                Metacard metacard = result.getMetacard();
                CDRMetacard cdrMetacard = new CDRMetacard(metacard);
                id = cdrMetacard.getId();
                entry = getMetacardEntry(cdrMetacard, properties);
                Double relevance = result.getRelevanceScore();

                if (relevance != null) {
                    Element relevanceElement = entry.addExtension(new QName(
                            AtomResponseConstants.RELEVANCE_NAMESPACE, AtomResponseConstants.RELEVANCE_ELEMENT,
                            AtomResponseConstants.RELEVANCE_NAMESPACE_PREFIX));
                    relevanceElement.setText(String.valueOf(relevance));
                }
                Double distance = result.getDistanceInMeters();
                if (distance != null) {
                    Element distanceElement = entry.addExtension(new QName(AtomResponseConstants.CDRS_EXT_NAMESPACE,
                            AtomResponseConstants.DISTANCE_ELEMENT,
                            AtomResponseConstants.CDRS_EXT_NAMESPACE_PREFIX));
                    distanceElement.setText(String.valueOf(distance));
                }
                feed.addEntry(entry);
            } catch (Exception e) {
                LOGGER.warn(
                        "WARNING - Could not properly transform metacard with id {} because ran into error '{}'.  Please turn on DEBUG logging for more details",
                        id, e.getMessage());
                LOGGER.debug(e.getMessage(), e);
            }
        }

        BinaryContent binaryContent = null;

        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            // Feed writeTo spins up a new thread so must do this
            currentClassLoader = Thread.currentThread().getContextClassLoader();
            try {
                Thread.currentThread().setContextClassLoader(AbstractAtomTransformer.class.getClassLoader());
                feed.writeTo(outputStream);
            } finally {
                Thread.currentThread().setContextClassLoader(currentClassLoader);
            }

            binaryContent = new BinaryContentImpl(new ByteArrayInputStream(outputStream.toByteArray()),
                    new MimeType(AtomResponseConstants.ATOM_MIME_TYPE));
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
        } catch (MimeTypeParseException e) {
            LOGGER.error(e.getMessage(), e);
        }
        return binaryContent;
    }

    protected void addStatus(SourceResponse response, Feed feed, List<Result> results,
            Map<String, Serializable> properties) {
        if (response instanceof QueryResponse) {
            QueryResponse queryResponse = (QueryResponse) response;
            Set<ProcessingDetails> details = queryResponse.getProcessingDetails();

            List<String> siteList = (List<String>) queryResponse.getPropertyValue("site-list");
            if (siteList == null) {
                siteList = new ArrayList<String>();
            }
            for (ProcessingDetails detail : details) {

                String sourceId = detail.getSourceId();
                siteList.remove(sourceId);

                ExtensibleElement sourceStatus = feed.addExtension(AtomResponseConstants.CDRB_NAMESPACE,
                        "sourceStatus", AtomResponseConstants.CDRB_NAMESPACE_PREFIX);
                sourceStatus.setAttributeValue(new QName(AtomResponseConstants.CDRB_NAMESPACE, "sourceId",
                        AtomResponseConstants.CDRB_NAMESPACE_PREFIX), sourceId);
                sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "shortName",
                        AtomResponseConstants.CDRB_NAMESPACE_PREFIX, sourceId);

                String feedPath = (String) properties.get(SearchConstants.PATH_PARAMETER);
                if (detail.hasException()) {
                    sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "status",
                            AtomResponseConstants.CDRB_NAMESPACE_PREFIX, "error");
                    sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "resultsRetrieved",
                            AtomResponseConstants.CDRB_NAMESPACE_PREFIX, "0");
                    if (StringUtils.isNotBlank(feedPath)) {
                        sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "path",
                                AtomResponseConstants.CDRB_NAMESPACE_PREFIX, feedPath);
                    }
                    sourceStatus.addSimpleExtension(AtomResponseConstants.CDRS_EXT_NAMESPACE, "warning",
                            AtomResponseConstants.CDRS_EXT_NAMESPACE_PREFIX, detail.getException().getMessage());
                    sourceStatus.addSimpleExtension(AtomResponseConstants.CDRS_EXT_NAMESPACE, "statusMessage",
                            AtomResponseConstants.CDRS_EXT_NAMESPACE_PREFIX, "Search complete with errors");
                } else {
                    Serializable object = queryResponse.getPropertyValue(sourceId);

                    if (object != null && object instanceof Map) {
                        Map<String, Serializable> sourceProperties = (Map<String, Serializable>) object;
                        Long elapsedTime = (Long) sourceProperties.get("elapsed-time");
                        if (elapsedTime != null) {
                            sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "elapsedTime",
                                    AtomResponseConstants.CDRB_NAMESPACE_PREFIX, String.valueOf(elapsedTime));
                        }

                        Long totalHits = (Long) sourceProperties.get("total-hits");
                        if (totalHits != null) {
                            sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "totalResults",
                                    AtomResponseConstants.CDRB_NAMESPACE_PREFIX, String.valueOf(totalHits));
                        }

                        Integer totalResultsReturned = (Integer) sourceProperties.get("total-results-returned");
                        if (totalResultsReturned != null) {
                            sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE,
                                    "resultsRetrieved", AtomResponseConstants.CDRB_NAMESPACE_PREFIX,
                                    String.valueOf(totalResultsReturned));
                        }
                    } else {
                        sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "resultsRetrieved",
                                AtomResponseConstants.CDRB_NAMESPACE_PREFIX, String.valueOf(results.size()));
                    }
                    sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "status",
                            AtomResponseConstants.CDRB_NAMESPACE_PREFIX, "complete");

                    if (StringUtils.isNotBlank(feedPath)) {
                        sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "path",
                                AtomResponseConstants.CDRB_NAMESPACE_PREFIX, feedPath);
                    }
                    sourceStatus.addSimpleExtension(AtomResponseConstants.CDRS_EXT_NAMESPACE, "statusMessage",
                            AtomResponseConstants.CDRS_EXT_NAMESPACE_PREFIX, "Search complete with no errors");
                    List<String> warnings = detail.getWarnings();
                    if (warnings != null && !warnings.isEmpty()) {
                        sourceStatus.addSimpleExtension(AtomResponseConstants.CDRS_EXT_NAMESPACE, "warning",
                                AtomResponseConstants.CDRS_EXT_NAMESPACE_PREFIX, warnings.get(0));
                    }
                }
            }

            if (details.isEmpty() && siteList.isEmpty()) {
                String sourceId = (String) properties.get(SearchConstants.LOCAL_SOURCE_ID);
                if (sourceId == null) {
                    sourceId = "SELF";
                }

                ExtensibleElement sourceStatus = feed.addExtension(AtomResponseConstants.CDRB_NAMESPACE,
                        "sourceStatus", AtomResponseConstants.CDRB_NAMESPACE_PREFIX);
                sourceStatus.setAttributeValue(new QName(AtomResponseConstants.CDRB_NAMESPACE, "sourceId",
                        AtomResponseConstants.CDRB_NAMESPACE_PREFIX), sourceId);
                sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "shortName",
                        AtomResponseConstants.CDRB_NAMESPACE_PREFIX, sourceId);
                sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "status",
                        AtomResponseConstants.CDRB_NAMESPACE_PREFIX, "complete");
                sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "resultsRetrieved",
                        AtomResponseConstants.CDRB_NAMESPACE_PREFIX, String.valueOf(results.size()));
                sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "totalResults",
                        AtomResponseConstants.CDRB_NAMESPACE_PREFIX, String.valueOf(response.getHits()));
                sourceStatus.addSimpleExtension(AtomResponseConstants.CDRS_EXT_NAMESPACE, "statusMessage",
                        AtomResponseConstants.CDRS_EXT_NAMESPACE_PREFIX, "Search complete with no errors");
            } else if (!siteList.isEmpty()) {
                for (String site : siteList) {
                    ExtensibleElement sourceStatus = feed.addExtension(AtomResponseConstants.CDRB_NAMESPACE,
                            "sourceStatus", AtomResponseConstants.CDRB_NAMESPACE_PREFIX);
                    sourceStatus.setAttributeValue(new QName(AtomResponseConstants.CDRB_NAMESPACE, "sourceId",
                            AtomResponseConstants.CDRB_NAMESPACE_PREFIX), site);
                    sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "shortName",
                            AtomResponseConstants.CDRB_NAMESPACE_PREFIX, site);

                    String feedPath = (String) properties.get(SearchConstants.PATH_PARAMETER);
                    Serializable object = queryResponse.getPropertyValue(site);
                    if (object != null && object instanceof Map) {
                        Map<String, Serializable> sourceProperties = (Map<String, Serializable>) object;
                        Long elapsedTime = (Long) sourceProperties.get("elapsed-time");
                        if (elapsedTime != null) {
                            sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "elapsedTime",
                                    AtomResponseConstants.CDRB_NAMESPACE_PREFIX, String.valueOf(elapsedTime));
                        }

                        Long totalHits = (Long) sourceProperties.get("total-hits");
                        if (totalHits != null) {
                            sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "totalResults",
                                    AtomResponseConstants.CDRB_NAMESPACE_PREFIX, String.valueOf(totalHits));
                        }

                        Integer totalResultsReturned = (Integer) sourceProperties.get("total-results-returned");
                        if (totalResultsReturned != null) {
                            sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE,
                                    "resultsRetrieved", AtomResponseConstants.CDRB_NAMESPACE_PREFIX,
                                    String.valueOf(totalResultsReturned));
                        }
                        sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "status",
                                AtomResponseConstants.CDRB_NAMESPACE_PREFIX, "complete");
                        sourceStatus.addSimpleExtension(AtomResponseConstants.CDRS_EXT_NAMESPACE, "statusMessage",
                                AtomResponseConstants.CDRS_EXT_NAMESPACE_PREFIX, "Search complete with no errors");
                    } else {
                        sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "resultsRetrieved",
                                AtomResponseConstants.CDRB_NAMESPACE_PREFIX, "0");
                        sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "status",
                                AtomResponseConstants.CDRB_NAMESPACE_PREFIX, "waiting");
                        sourceStatus.addSimpleExtension(AtomResponseConstants.CDRS_EXT_NAMESPACE, "statusMessage",
                                AtomResponseConstants.CDRS_EXT_NAMESPACE_PREFIX,
                                "Source is still being searched, and has not returned results yet");
                    }

                    if (StringUtils.isNotBlank(feedPath)) {
                        sourceStatus.addSimpleExtension(AtomResponseConstants.CDRB_NAMESPACE, "path",
                                AtomResponseConstants.CDRB_NAMESPACE_PREFIX, feedPath);
                    }

                }
            }

        }
    }

    protected boolean isFalse(Boolean includeStatus) {
        return Boolean.FALSE.equals(includeStatus);
    }

    @Override
    public BinaryContent transform(Metacard metacard, Map<String, Serializable> properties)
            throws CatalogTransformerException {
        if (properties == null) {
            properties = new HashMap<String, Serializable>();
        }
        BinaryContent binaryContent = null;
        try {
            Entry entry = getMetacardEntry(new CDRMetacard(metacard), properties);

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            entry.writeTo(outputStream);
            binaryContent = new BinaryContentImpl(new ByteArrayInputStream(outputStream.toByteArray()),
                    new MimeType(AtomResponseConstants.ATOM_MIME_TYPE));
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
        } catch (MimeTypeParseException e) {
            LOGGER.error(e.getMessage(), e);
        }
        return binaryContent;
    }

    protected ActionProvider getViewMetacardActionProvider() {
        return viewMetacardActionProvider;
    }

    protected ActionProvider getMetadataActionProvider() {
        return metadataActionProvider;
    }

    protected ActionProvider getThumbnailActionProvider() {
        return thumbnailActionProvider;
    }

    protected ActionProvider getResourceActionProvider() {
        return resourceActionProvider;
    }

    protected MimeType getThumbnailMimeType() {
        return thumbnailMimeType;
    }

    protected MimeType getViewMimeType() {
        return viewMimeType;
    }

    protected boolean isTransform41() {
        return isTransform41;
    }

    protected boolean isTransform50() {
        return isTransform50;
    }

    protected Entry getMetacardEntry(CDRMetacard metacard, Map<String, Serializable> properties)
            throws URIException {

        String format = (String) properties.get(SearchConstants.FORMAT_PARAMETER);

        Entry entry = Abdera.getInstance().newEntry();

        entry.declareNS(AtomResponseConstants.GEORSS_NAMESPACE, AtomResponseConstants.GEORSS_NAMESPACE_PREFIX);
        entry.declareNS(AtomResponseConstants.RELEVANCE_NAMESPACE,
                AtomResponseConstants.RELEVANCE_NAMESPACE_PREFIX);

        entry.setId(URIUtil.encodePathQuery(metacard.getAtomId()));
        entry.setTitle(metacard.getTitle());
        entry.setUpdated(metacard.getModifiedDate());
        Date effective = metacard.getEffectiveDate();
        if (effective != null) {
            entry.setPublished(effective);
        }

        entry.addCategory(metacard.getContentTypeVersion(), metacard.getContentTypeName(), "Content Type");

        String sourceId = metacard.getSourceId();
        if (sourceId != null) {
            ExtensibleElement element = entry.addExtension(AtomResponseConstants.CDRB_NAMESPACE,
                    AtomResponseConstants.RESULT_SOURCE_ELEMENT, AtomResponseConstants.CDRB_NAMESPACE_PREFIX);
            element.setAttributeValue(new QName(AtomResponseConstants.CDRB_NAMESPACE, "sourceId",
                    AtomResponseConstants.CDRB_NAMESPACE_PREFIX), sourceId);
            element.setText(sourceId);
        }

        addLinksToEntry(entry, metacard, format, properties);

        if (metacard.hasLocation()) {
            addLocation(entry, metacard, useGmlEncoding(properties));
        }

        Date createdDate = metacard.getCreatedDate();
        if (createdDate != null) {
            entry.addSimpleExtension(AtomResponseConstants.METACARD_ATOM_NAMESPACE,
                    AtomResponseConstants.METACARD_CREATED_DATE_ELEMENT,
                    AtomResponseConstants.METACARD_ATOM_NAMESPACE_PREFIX,
                    DATE_FORMATTER.print(createdDate.getTime()));
        }

        Date expirationDate = metacard.getExpirationDate();
        if (expirationDate != null) {
            entry.addSimpleExtension(AtomResponseConstants.METACARD_ATOM_NAMESPACE,
                    AtomResponseConstants.METADATA_EXPIRATION_DATE_ELEMENT,
                    AtomResponseConstants.METACARD_ATOM_NAMESPACE_PREFIX,
                    DATE_FORMATTER.print(expirationDate.getTime()));
        }

        addEntryElements(entry, metacard, properties);

        return entry;
    }

    /**
     * This method inspects the properties to determine if there is a property that specifies the GeoRSS format. If that
     * property exists then it will use the value of that property to determine whether to use simple or gml format.
     * Otherwise it will return the default global property for using GML or Simple
     *
     * @param properties
     *            that were passed into the transformer
     * @return true if GML encoding for GeoRSS should be used, false would mean to use simple encoding
     */
    protected boolean useGmlEncoding(Map<String, Serializable> properties) {
        String format = (String) properties.get(SearchConstants.GEORSS_RESULT_FORMAT_PARAMETER);
        boolean useGml = defaultToUseGMLEncoding;
        if (StringUtils.isNotBlank(format)) {
            if (SearchConstants.GEORSS_SIMPLE_FORMAT.equalsIgnoreCase(format)) {
                useGml = false;
            } else if (SearchConstants.GEORSS_GML_FORMAT.equalsIgnoreCase(format)) {
                useGml = true;
            }
        }
        return useGml;
    }

    protected void addLinksToEntry(Entry entry, CDRMetacard metacard, String format,
            Map<String, Serializable> properties) {
        LOGGER.debug("Metacard with ID {} hasThumbail={} with thumbnailActionProvider={}", metacard.getId(),
                metacard.hasThumbnail(), thumbnailActionProvider);
        if (metacard.hasThumbnail()) {
            if (thumbnailActionProvider != null) {
                Action action = thumbnailActionProvider.getAction(metacard);
                LOGGER.debug("Thumbnail action={}", action);
                if (action != null && action.getUrl() != null) {
                    entry.addLink(action.getUrl().toString(), SearchConstants.LINK_REL_PREVIEW,
                            thumbnailMimeType.getBaseType(), action.getTitle(), null,
                            metacard.getThumbnailLength());
                }
            }
        }
        if (resourceActionProvider != null && metacard.hasResource()) {
            Action action = resourceActionProvider.getAction(metacard);
            if (action != null && action.getUrl() != null) {
                entry.addLink(action.getUrl().toString(), Link.REL_ALTERNATE, metacard.getResourceMIMETypeString(),
                        action.getTitle(), null, metacard.getResourceSizeLong());
            }
            // If there is no explicit resource then the metadata serves as
            // the product/resource so include a link to it here
        } else if (metadataActionProvider != null) {

            Action action = metadataActionProvider.getAction(metacard);
            if (action != null && action.getUrl() != null) {
                entry.addLink(action.getUrl().toString(), Link.REL_ALTERNATE, "text/xml", "View Product", null, -1);
            }
        }
        if (viewMetacardActionProvider != null) {
            Action action = viewMetacardActionProvider.getAction(metacard);
            if (action != null && action.getUrl() != null) {
                String transformFormat = (String) properties.get(SearchConstants.METACARD_TRANSFORMER_NAME);
                if (StringUtils.isBlank(transformFormat)) {
                    transformFormat = StringUtils.defaultIfBlank(format, CDR_ATOM_TRANSFORMER_ID);
                }
                entry.addLink(action.getUrl().toString() + "?transform=" + transformFormat, Link.REL_SELF,
                        AtomResponseConstants.ATOM_MIME_TYPE, "View Atom Entry", null, -1);
                entry.addLink(action.getUrl().toString(), Link.REL_RELATED, "text/xml", action.getTitle(), null,
                        -1);
            }
        }
    }

    protected void addLocation(Entry entry, Metacard metacard, boolean useGmlEncoding) {
        WKTReader reader = new WKTReader();
        try {
            Geometry geo = reader.read(metacard.getLocation());

            List<Position> positions = CompositeGeometry.getCompositeGeometry(geo).toGeoRssPositions();
            for (Position position : positions) {
                if (useGmlEncoding) {
                    GeoHelper.addPosition(entry, position, GeoHelper.Encoding.GML);
                } else {
                    GeoHelper.addPosition(entry, position, GeoHelper.Encoding.SIMPLE);
                }
            }
        } catch (ParseException e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    protected void addLinksToFeed(Feed feed, Map<String, Serializable> properties) {
        Serializable property = properties.get(SearchConstants.SELF_LINK_REL);
        if (property != null && property instanceof String) {
            feed.addLink(String.valueOf(property), Link.REL_SELF, AtomResponseConstants.ATOM_MIME_TYPE,
                    "Current Page", null, -1);
        }

        property = properties.get(SearchConstants.FIRST_LINK_REL);
        if (property != null && property instanceof String) {
            feed.addLink(String.valueOf(property), Link.REL_FIRST, AtomResponseConstants.ATOM_MIME_TYPE,
                    "First Page", null, -1);
        }

        property = properties.get(SearchConstants.LAST_LINK_REL);
        if (property != null && property instanceof String) {
            feed.addLink(String.valueOf(property), Link.REL_LAST, AtomResponseConstants.ATOM_MIME_TYPE, "Last Page",
                    null, -1);
        }

        property = properties.get(SearchConstants.NEXT_LINK_REL);
        if (property != null && property instanceof String) {
            feed.addLink(String.valueOf(property), Link.REL_NEXT, AtomResponseConstants.ATOM_MIME_TYPE, "Next Page",
                    null, -1);
        }

        property = properties.get(SearchConstants.PREV_LINK_REL);
        if (property != null && property instanceof String) {
            feed.addLink(String.valueOf(property), Link.REL_PREVIOUS, AtomResponseConstants.ATOM_MIME_TYPE,
                    "Previous Page", null, -1);
        }

    }

    protected void setFeedTitle(Feed feed, Map<String, Serializable> properties) {
        Serializable property = properties.get(SearchConstants.FEED_TITLE);
        if (property != null && property instanceof String) {
            feed.setTitle(String.valueOf(property));
        } else {
            feed.setTitle("Atom Search Results Feed from source:" + SystemInfo.getSiteName());
        }

    }

    protected void setFeedSecurity(Feed feed, String format) {
        LOGGER.debug("Setting feed security for atom feed with format [{}]", format);
        SecurityConfiguration securityConfiguration = null;
        if (StringUtils.isNotBlank(format)) {
            securityConfiguration = getConfigurationFromFormat(format);
        }

        if (securityConfiguration == null) {
            LOGGER.debug(
                    "No valid security configuration found for format [{}], using default configurations specified by format [{}]",
                    format, SecurityConfiguration.DEFAULT_FORMAT_CONFIGURATION);
            securityConfiguration = getConfigurationFromFormat(SecurityConfiguration.DEFAULT_FORMAT_CONFIGURATION);
        }
        if (securityConfiguration != null) {
            String namespace = securityConfiguration.getNamespace();
            if (StringUtils.isNotBlank(namespace)) {
                Map<String, String> securityAttributes = securityConfiguration.getAttributes();
                if (securityAttributes != null && !securityAttributes.isEmpty()) {
                    feed.declareNS(namespace, SecurityConstants.ISM_NAMESPACE_PREFIX);
                    for (java.util.Map.Entry<String, String> securityEntry : securityAttributes.entrySet()) {
                        String securityValue = securityEntry.getValue();
                        if (StringUtils.isNotBlank(securityValue)) {
                            feed.setAttributeValue(new QName(namespace, securityEntry.getKey()),
                                    securityEntry.getValue());
                        }
                    }
                }
            }
        } else {
            LOGGER.debug(
                    "Could not find a SecurityConfiguration for the format [{}], not populating feed security markings.",
                    format);
        }
    }

    protected void setEntrySecurity(Entry entry, Metacard metacard) {
        for (SecurityMarkingHandler securityHandler : securityHandlers) {
            try {
                SecurityData securityData = securityHandler.getSecurityData(metacard);
                if (securityData != null) {
                    String securityNamespace = securityData.getSecurityNamespace();
                    if (StringUtils.isNotBlank(securityNamespace)) {
                        boolean hasAttribute = false;
                        for (java.util.Map.Entry<String, List<String>> securityEntry : securityData
                                .getSecurityMarkings().entrySet()) {
                            List<String> securityValues = securityEntry.getValue();
                            if (securityValues != null && !securityValues.isEmpty()) {
                                if (!hasAttribute) {
                                    entry.declareNS(securityNamespace, SecurityConstants.ISM_NAMESPACE_PREFIX);
                                }
                                entry.setAttributeValue(new QName(securityNamespace, securityEntry.getKey()),
                                        StringUtils.join(securityValues, " "));
                                hasAttribute = true;
                            }
                        }
                        if (hasAttribute) {
                            break;
                        }
                    }
                }
            } catch (Exception e) {
                LOGGER.debug(
                        "Ran into exception when trying to parse security info using SecurityMarkingHander of type {}, skipping and trying next handler",
                        securityHandler.getClass(), e);
            }
        }
    }

    private SecurityConfiguration getConfigurationFromFormat(String format) {
        SecurityConfiguration securityConfiguration = null;
        for (SecurityConfiguration curConfig : securityConfigurations) {
            if (curConfig.getFormats().contains(format)) {
                securityConfiguration = curConfig;
                break;
            }
        }
        return securityConfiguration;
    }

}