/** * 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; } }