/** * Copyright (c) Codice Foundation * <p/> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p/> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package ddf.catalog.impl; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Dictionary; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.builder.ToStringBuilder; import org.codice.ddf.configuration.SystemInfo; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ddf.catalog.CatalogFramework; import ddf.catalog.content.data.ContentItem; import ddf.catalog.content.operation.CreateStorageRequest; import ddf.catalog.content.operation.UpdateStorageRequest; import ddf.catalog.data.BinaryContent; import ddf.catalog.data.Metacard; import ddf.catalog.data.MetacardType; import ddf.catalog.data.impl.BasicTypes; import ddf.catalog.federation.FederationException; import ddf.catalog.federation.FederationStrategy; import ddf.catalog.impl.operations.CreateOperations; import ddf.catalog.impl.operations.DeleteOperations; import ddf.catalog.impl.operations.QueryOperations; import ddf.catalog.impl.operations.ResourceOperations; import ddf.catalog.impl.operations.SourceOperations; import ddf.catalog.impl.operations.TransformOperations; import ddf.catalog.impl.operations.UpdateOperations; import ddf.catalog.operation.CreateRequest; import ddf.catalog.operation.CreateResponse; import ddf.catalog.operation.DeleteRequest; import ddf.catalog.operation.DeleteResponse; import ddf.catalog.operation.QueryRequest; import ddf.catalog.operation.QueryResponse; import ddf.catalog.operation.ResourceRequest; import ddf.catalog.operation.ResourceResponse; import ddf.catalog.operation.SourceInfoRequest; import ddf.catalog.operation.SourceInfoResponse; import ddf.catalog.operation.SourceResponse; import ddf.catalog.operation.UpdateRequest; import ddf.catalog.operation.UpdateResponse; import ddf.catalog.resource.ResourceNotFoundException; import ddf.catalog.resource.ResourceNotSupportedException; import ddf.catalog.source.IngestException; import ddf.catalog.source.SourceUnavailableException; import ddf.catalog.source.UnsupportedQueryException; import ddf.catalog.transform.CatalogTransformerException; import ddf.catalog.util.impl.DescribableImpl; import ddf.catalog.util.impl.Masker; /** * CatalogFrameworkImpl is the core class of DDF. It is used for query, create, update, delete, and * resource retrieval operations. */ @SuppressWarnings("deprecation") public class CatalogFrameworkImpl extends DescribableImpl implements CatalogFramework { private static final Logger LOGGER = LoggerFactory.getLogger(CatalogFrameworkImpl.class); private static final String FANOUT_MESSAGE = "Fanout proxy does not support create, update, and delete operations"; // // Injected properties // private boolean fanoutEnabled; private List<String> fanoutTagBlacklist = new ArrayList<>(); private Masker masker; private CreateOperations createOperations; private UpdateOperations updateOperations; private DeleteOperations deleteOperations; private QueryOperations queryOperations; private ResourceOperations resourceOperations; private SourceOperations sourceOperations; private TransformOperations transformOperations; /** * Instantiates a new CatalogFrameworkImpl which delegates its work to surrogate operations classes. * * @param createOperations delegate that handles create operations * @param updateOperations delegate that handles update operations * @param deleteOperations delegate that handles delete operations * @param queryOperations delegate that handles query operations * @param resourceOperations delegate that handles resource operations * @param sourceOperations delegate that handles source operations * @param transformOperations delegate that handles transformation operations */ public CatalogFrameworkImpl(CreateOperations createOperations, UpdateOperations updateOperations, DeleteOperations deleteOperations, QueryOperations queryOperations, ResourceOperations resourceOperations, SourceOperations sourceOperations, TransformOperations transformOperations) { this.createOperations = createOperations; this.updateOperations = updateOperations; this.deleteOperations = deleteOperations; this.queryOperations = queryOperations; this.resourceOperations = resourceOperations; this.sourceOperations = sourceOperations; this.transformOperations = transformOperations; setId(SystemInfo.getSiteName()); setVersion(SystemInfo.getVersion()); setOrganization(SystemInfo.getOrganization()); registerBasicMetacard(); } private void registerBasicMetacard() { Bundle bundle = FrameworkUtil.getBundle(CatalogFrameworkImpl.class); if (bundle != null && bundle.getBundleContext() != null) { Dictionary<String, Object> properties = new Hashtable<>(); properties.put("name", BasicTypes.BASIC_METACARD.getName()); bundle.getBundleContext() .registerService(MetacardType.class, BasicTypes.BASIC_METACARD, properties); } } public QueryOperations getQueryOperations() { return queryOperations; } public ResourceOperations getResourceOperations() { return resourceOperations; } public SourceOperations getSourceOperations() { return sourceOperations; } public TransformOperations getTransformOperations() { return transformOperations; } public void setFanoutEnabled(boolean fanoutEnabled) { this.fanoutEnabled = fanoutEnabled; } public void setFanoutTagBlacklist(List<String> fanoutTagBlacklist) { this.fanoutTagBlacklist = fanoutTagBlacklist; } /** * Sets the {@link Masker} * * @param masker the {@link Masker} this framework will use */ public void setMasker(Masker masker) { synchronized (this) { this.masker = masker; if (this.getId() != null) { masker.setId(getId()); } } } /** * Sets the source id to identify this framework (DDF). This is also referred to as the site * name. * * @param sourceId the sourceId to set */ @Override public void setId(String sourceId) { LOGGER.debug("Setting id = {}", sourceId); synchronized (this) { super.setId(sourceId); if (masker != null) { masker.setId(sourceId); } // Set the id of the describable delegate objects if (queryOperations != null) { queryOperations.setId(sourceId); } if (resourceOperations != null) { resourceOperations.setId(sourceId); } if (sourceOperations != null) { sourceOperations.setId(sourceId); } } } @Override public Set<String> getSourceIds() { return sourceOperations.getSourceIds(fanoutEnabled); } @Override public SourceInfoResponse getSourceInfo(SourceInfoRequest sourceInfoRequest) throws SourceUnavailableException { return sourceOperations.getSourceInfo(sourceInfoRequest, fanoutEnabled); } @Override public CreateResponse create(CreateStorageRequest createRequest) throws IngestException, SourceUnavailableException { List<String> blacklist = Collections.emptyList(); if (fanoutEnabled) { blacklist = new ArrayList<>(fanoutTagBlacklist); } return createOperations.create(createRequest, blacklist); } @Override public CreateResponse create(CreateRequest createRequest) throws IngestException, SourceUnavailableException { if (fanoutEnabled && blockFanoutCreate(createRequest)) { throw new IngestException(FANOUT_MESSAGE); } return createOperations.create(createRequest); } @Override public UpdateResponse update(UpdateStorageRequest updateRequest) throws IngestException, SourceUnavailableException { if (fanoutEnabled && blockFanoutStorageRequest(updateRequest)) { throw new IngestException(FANOUT_MESSAGE); } return updateOperations.update(updateRequest); } @Override public UpdateResponse update(UpdateRequest updateRequest) throws IngestException, SourceUnavailableException { if (fanoutEnabled && blockFanoutUpdate(updateRequest)) { throw new IngestException(FANOUT_MESSAGE); } return updateOperations.update(updateRequest); } @Override public DeleteResponse delete(DeleteRequest deleteRequest) throws IngestException, SourceUnavailableException { List<String> blacklist = Collections.emptyList(); if (fanoutEnabled) { blacklist = new ArrayList<>(fanoutTagBlacklist); } return deleteOperations.delete(deleteRequest, blacklist); } @Override public QueryResponse query(QueryRequest fedQueryRequest) throws UnsupportedQueryException, SourceUnavailableException, FederationException { return queryOperations.query(fedQueryRequest, fanoutEnabled); } @Override public QueryResponse query(QueryRequest queryRequest, FederationStrategy strategy) throws SourceUnavailableException, UnsupportedQueryException, FederationException { return queryOperations.query(queryRequest, strategy, fanoutEnabled); } @Override public BinaryContent transform(Metacard metacard, String transformerShortname, Map<String, Serializable> arguments) throws CatalogTransformerException { return transformOperations.transform(metacard, transformerShortname, arguments); } @Override public BinaryContent transform(SourceResponse response, String transformerShortname, Map<String, Serializable> arguments) throws CatalogTransformerException { return transformOperations.transform(response, transformerShortname, arguments); } @Override public ResourceResponse getLocalResource(ResourceRequest resourceRequest) throws IOException, ResourceNotFoundException, ResourceNotSupportedException { return resourceOperations.getLocalResource(resourceRequest, fanoutEnabled); } @Override public ResourceResponse getResource(ResourceRequest resourceRequest, String resourceSiteName) throws IOException, ResourceNotFoundException, ResourceNotSupportedException { return resourceOperations.getResource(resourceRequest, resourceSiteName, fanoutEnabled); } @Override public ResourceResponse getEnterpriseResource(ResourceRequest resourceRequest) throws IOException, ResourceNotFoundException, ResourceNotSupportedException { return resourceOperations.getEnterpriseResource(resourceRequest, fanoutEnabled); } @Deprecated @Override public Map<String, Set<String>> getLocalResourceOptions(String metacardId) throws ResourceNotFoundException { return resourceOperations.getLocalResourceOptions(metacardId, fanoutEnabled); } @Deprecated @Override public Map<String, Set<String>> getEnterpriseResourceOptions(String metacardId) throws ResourceNotFoundException { return resourceOperations.getEnterpriseResourceOptions(metacardId, fanoutEnabled); } @Deprecated @Override public Map<String, Set<String>> getResourceOptions(String metacardId, String sourceId) throws ResourceNotFoundException { return resourceOperations.getResourceOptions(metacardId, sourceId, fanoutEnabled); } /** * String representation of this {@code CatalogFrameworkImpl}. */ @Override public String toString() { return ToStringBuilder.reflectionToString(this); } private boolean blockFanoutStorageRequest(UpdateStorageRequest updateStorageRequest) { return blockFanoutContentItems(updateStorageRequest.getContentItems()); } private boolean blockFanoutContentItems(List<ContentItem> contentItems) { return contentItems.stream() .map(ContentItem::getMetacard) .anyMatch(this::isMetacardBlacklisted); } private boolean blockFanoutCreate(CreateRequest createRequest) { return createRequest.getMetacards() .stream() .anyMatch(this::isMetacardBlacklisted); } private boolean blockFanoutUpdate(UpdateRequest updateRequest) { return updateRequest.getUpdates() .stream() .anyMatch((updateEntry) -> isMetacardBlacklisted(updateEntry.getValue())); } private boolean isMetacardBlacklisted(Metacard metacard) { Set<String> tags = new HashSet<>(metacard.getTags()); // defaulting to resource tag if the metacard doesn't contain any tags if (tags.isEmpty()) { tags.add(Metacard.DEFAULT_TAG); } return CollectionUtils.containsAny(tags, fanoutTagBlacklist); } }