/**
* 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.content.plugin.cataloger;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import javax.activation.MimeType;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.LoggerFactory;
import org.slf4j.ext.XLogger;
import com.google.common.io.FileBackedOutputStream;
import ddf.catalog.CatalogFramework;
import ddf.catalog.data.BinaryContent;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.MetacardCreationException;
import ddf.catalog.data.impl.AttributeImpl;
import ddf.catalog.transform.CatalogTransformerException;
import ddf.catalog.transform.InputTransformer;
import ddf.content.data.ContentItem;
import ddf.content.operation.CreateResponse;
import ddf.content.operation.DeleteResponse;
import ddf.content.operation.UpdateResponse;
import ddf.content.operation.impl.CreateResponseImpl;
import ddf.content.operation.impl.DeleteResponseImpl;
import ddf.content.operation.impl.UpdateResponseImpl;
import ddf.content.plugin.ContentPlugin;
import ddf.content.plugin.PluginExecutionException;
import ddf.mime.MimeTypeToTransformerMapper;
import ddf.security.SubjectUtils;
public class CatalogContentPlugin implements ContentPlugin {
private static final XLogger LOGGER = new XLogger(
LoggerFactory.getLogger(CatalogContentPlugin.class));
private static final String CATALOG_ID = "Catalog-ID";
private static final String DEFAULT_METACARD_TRANSFORMER = "geojson";
private final CatalogFramework catalogFramework;
private Cataloger cataloger;
private MimeTypeToTransformerMapper mimeTypeToTransformerMapper;
public CatalogContentPlugin(CatalogFramework catalogFramework,
MimeTypeToTransformerMapper mimeTypeToTransformerMapper) {
LOGGER.trace("INSIDE: CatalogContentPlugin constructor");
this.catalogFramework = catalogFramework;
this.cataloger = new Cataloger(catalogFramework);
this.mimeTypeToTransformerMapper = mimeTypeToTransformerMapper;
}
@Override
public CreateResponse process(CreateResponse input) throws PluginExecutionException {
LOGGER.trace("ENTERING: process(CreateResponse)");
ContentItem createdContentItem = input.getCreatedContentItem();
CreateResponseImpl response = new CreateResponseImpl(input);
MimeType mimeType = createdContentItem.getMimeType();
InputStream stream = null;
try {
stream = createdContentItem.getInputStream();
} catch (IOException e) {
throw new PluginExecutionException(
"Unable to read InputStream in created content item.", e);
}
if (stream == null) {
throw new PluginExecutionException("InputStream is null in created content item.");
}
try {
Metacard metacard = generateMetacard(createdContentItem, mimeType,
createdContentItem.getUri(), stream);
String catalogId = cataloger.createMetacard(metacard);
LOGGER.debug("catalogId = " + catalogId);
Map<String, String> properties = response.getResponseProperties();
properties.put(CATALOG_ID, catalogId);
response.setResponseProperties(properties);
if (metacard != null) {
try {
BinaryContent binaryContent = catalogFramework
.transform(metacard, DEFAULT_METACARD_TRANSFORMER, null);
response.setCreatedMetadata(binaryContent.getByteArray());
response.setCreatedMetadataMimeType(binaryContent.getMimeType().toString());
} catch (IOException | CatalogTransformerException e) {
LOGGER.warn("Unable to transform metacard to readable metadata.", e);
}
}
} catch (MetacardCreationException e) {
LOGGER.warn(e.getMessage(), e);
throw new PluginExecutionException(e.getMessage(), e);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
LOGGER.trace("EXITING: process(CreateResponse)");
return response;
}
@Override
public UpdateResponse process(UpdateResponse input) throws PluginExecutionException {
LOGGER.trace("ENTERING: process(UpdateResponse)");
ContentItem updatedContentItem = input.getUpdatedContentItem();
UpdateResponseImpl response = new UpdateResponseImpl(input);
MimeType mimeType = updatedContentItem.getMimeType();
InputStream stream = null;
try {
stream = updatedContentItem.getInputStream();
} catch (IOException e) {
throw new PluginExecutionException(
"Unable to read InputStream in updated content item.", e);
}
if (stream == null) {
throw new PluginExecutionException("InputStream is null in updated content item.");
}
try {
Metacard metacard = generateMetacard(updatedContentItem, mimeType,
updatedContentItem.getUri(), stream);
String catalogId = cataloger.updateMetacard(updatedContentItem.getUri(), metacard);
LOGGER.debug("catalogId = " + catalogId);
Map<String, String> properties = response.getResponseProperties();
properties.put(CATALOG_ID, catalogId);
response.setResponseProperties(properties);
if (metacard != null) {
try {
BinaryContent binaryContent = catalogFramework
.transform(metacard, DEFAULT_METACARD_TRANSFORMER, null);
response.setUpdatedMetadata(binaryContent.getByteArray());
response.setUpdatedMetadataMimeType(binaryContent.getMimeType().toString());
} catch (IOException | CatalogTransformerException e) {
LOGGER.warn("Unable to transform metacard to readable metadata.", e);
}
}
} catch (MetacardCreationException e) {
LOGGER.warn(e.getMessage(), e);
throw new PluginExecutionException(e.getMessage(), e);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
LOGGER.trace("EXITING: process(UpdateResponse)");
return response;
}
@Override
public DeleteResponse process(DeleteResponse input) throws PluginExecutionException {
LOGGER.trace("ENTERING: process(DeleteResponse)");
DeleteResponseImpl response = new DeleteResponseImpl(input);
String catalogId = cataloger.deleteMetacard(input.getContentItem().getUri());
if (catalogId != null && !catalogId.isEmpty()) {
// Create response indicating file (actually, catalog entry) was deleted
response = new DeleteResponseImpl(input.getRequest(), input.getContentItem(), true,
input.getResponseProperties(), input.getProperties());
}
LOGGER.debug("catalogId = " + catalogId);
Map<String, String> properties = response.getResponseProperties();
properties.put(CATALOG_ID, catalogId);
response.setResponseProperties(properties);
LOGGER.trace("EXITING: process(DeleteResponse)");
return response;
}
private Metacard generateMetacard(ContentItem contentItem, MimeType mimeType, String uri,
InputStream message) throws MetacardCreationException {
LOGGER.trace("ENTERING: generateMetacard");
List<InputTransformer> listOfCandidates = mimeTypeToTransformerMapper
.findMatches(InputTransformer.class, mimeType);
LOGGER.debug("List of matches for mimeType [ {} ]: {}", mimeType, listOfCandidates);
Metacard generatedMetacard = null;
try (FileBackedOutputStream fileBackedOutputStream = new FileBackedOutputStream(1000000)) {
long size;
try {
size = IOUtils.copyLarge(message, fileBackedOutputStream);
LOGGER.debug("Copied {} bytes of file in content framework", size);
} catch (IOException e) {
throw new MetacardCreationException("Could not copy bytes of content message.", e);
}
// Multiple InputTransformers may be found that match the mime type.
// Need to try each InputTransformer until we find one that can successfully transform
// the input stream's data into a metacard. Once an InputTransformer is found that
// can create the metacard, then do not need to try any remaining InputTransformers.
for (InputTransformer transformer : listOfCandidates) {
try (InputStream inputStreamMessageCopy = fileBackedOutputStream.asByteSource()
.openStream()) {
generatedMetacard = transformer.transform(inputStreamMessageCopy);
if (generatedMetacard != null) {
try {
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
generatedMetacard.setAttribute(
new AttributeImpl(Metacard.POINT_OF_CONTACT,
SubjectUtils.getName(subject)));
}
} catch (IllegalStateException e) {
LOGGER.debug("Unable to retrieve user from request.", e);
}
if (uri != null) {
//Setting the non-transformer specific information not including creation and modification dates/times
generatedMetacard
.setAttribute(new AttributeImpl(Metacard.RESOURCE_URI, uri));
generatedMetacard.setAttribute(new AttributeImpl(Metacard.RESOURCE_SIZE,
String.valueOf(size)));
} else {
LOGGER.debug("Metacard had a null uri");
}
if (StringUtils.isBlank(generatedMetacard.getTitle())) {
LOGGER.debug("Metacard title was blank. Setting title to filename.");
generatedMetacard.setAttribute(
new AttributeImpl(Metacard.TITLE, contentItem.getFilename()));
}
break;
}
} catch (IOException | CatalogTransformerException e) {
LOGGER.debug("Transformer [" + transformer + "] could not create metacard.", e);
}
}
if (generatedMetacard == null) {
throw new MetacardCreationException(
"Could not create metacard with mimeType " + mimeType
+ ". No valid transformers found.");
}
LOGGER.trace("EXITING: generateMetacard");
} catch (IOException e) {
LOGGER.debug("Error encountered while using filed backed stream.", e);
}
return generatedMetacard;
}
}