/** * 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.impl; import java.io.IOException; import java.util.List; import java.util.UUID; import org.osgi.framework.BundleContext; import org.slf4j.LoggerFactory; import org.slf4j.ext.XLogger; import ddf.content.ContentFramework; import ddf.content.ContentFrameworkException; import ddf.content.data.ContentItem; import ddf.content.data.impl.IncomingContentItem; import ddf.content.operation.CreateRequest; import ddf.content.operation.CreateResponse; import ddf.content.operation.DeleteRequest; import ddf.content.operation.DeleteResponse; import ddf.content.operation.ReadRequest; import ddf.content.operation.ReadResponse; import ddf.content.operation.Request; import ddf.content.operation.Request.Directive; import ddf.content.operation.UpdateRequest; import ddf.content.operation.UpdateResponse; import ddf.content.operation.impl.CreateRequestImpl; import ddf.content.operation.impl.CreateResponseImpl; import ddf.content.operation.impl.DeleteRequestImpl; import ddf.content.operation.impl.DeleteResponseImpl; import ddf.content.operation.impl.ReadRequestImpl; import ddf.content.operation.impl.UpdateResponseImpl; import ddf.content.plugin.ContentPlugin; import ddf.content.plugin.PluginExecutionException; import ddf.content.storage.StorageException; import ddf.content.storage.StorageProvider; /** * ContentFrameworkImpl is the core class of the DDF Content Framework. It is used for create, * update, delete, and content retrieval operations for content stored in the DDF Content * Repository. */ public class ContentFrameworkImpl implements ContentFramework { private static final XLogger LOGGER = new XLogger( LoggerFactory.getLogger(ContentFrameworkImpl.class)); /** * The {@link List} of content plugins to execute on the ingest response after content has been * created, updated, or deleted in the content repository. */ protected List<ContentPlugin> contentPlugins; private BundleContext context; private StorageProvider provider; /** * Instantiates a new ContentFrameworkImpl, usually invoked from blueprint. * * @param context The BundleContext that will be utilized by this instance. * @param provider The {@link StorageProvider} used for read, create, update, and delete operations. * @param contentPlugins A list of {@link ContentPlugin}(s) that will be invoked after the ingest * operation. */ public ContentFrameworkImpl(BundleContext context, StorageProvider provider, List<ContentPlugin> contentPlugins) { LOGGER.trace("ENTERING: ContentFrameworkImpl constructor"); this.context = context; this.provider = provider; this.contentPlugins = contentPlugins; LOGGER.trace("EXITING: ContentFrameworkImpl constructor"); } @Override public CreateResponse create(CreateRequest createRequest, Request.Directive directive) throws ContentFrameworkException { LOGGER.trace("ENTERING: create"); LOGGER.debug("directive = " + directive); CreateResponse createResponse = null; // If directive includes processing and there are no ContentPlugins currently installed to // support processing, then throw an exception. (Do not want to do the STORE and get the // content repository out of sync with the Metadata Catalog.) if ((directive == Directive.PROCESS || directive == Directive.STORE_AND_PROCESS) && ( this.contentPlugins.size() == 0)) { throw new ContentFrameworkException( "Unable to perform " + directive + " because no ContentPlugins are installed."); } // Recreate content item so can add GUID to request ContentItem incomingContentItem = createRequest.getContentItem(); String id = UUID.randomUUID().toString().replaceAll("-", ""); LOGGER.debug("Created GUID: " + id); try { ContentItem contentItem = new IncomingContentItem(id, incomingContentItem.getInputStream(), incomingContentItem.getMimeTypeRawData(), incomingContentItem.getFilename()); contentItem.setUri(incomingContentItem.getUri()); createRequest = new CreateRequestImpl(contentItem, createRequest.getProperties()); } catch (IOException e1) { throw new ContentFrameworkException("Unable to add ID to IncomingContentItem", e1); } if (directive == Directive.STORE || directive == Directive.STORE_AND_PROCESS) { try { createResponse = provider.create(createRequest); } catch (StorageException e) { throw new ContentFrameworkException(e); } catch (Exception e) { LOGGER.warn("Content Provider error during create", e); throw new ContentFrameworkException( "Unable to perform create because no content storage provider is installed or there is a problem with the content storage provider.", e); } } if (directive == Directive.PROCESS || directive == Directive.STORE_AND_PROCESS) { if (directive == Directive.PROCESS) { // No content storage occurred to return a CreateResponse. So need to // instantiate a CreateResponse with the original CreateRequest's ContentItem // in it. if (createResponse == null) { createResponse = new CreateResponseImpl(createRequest, createRequest.getContentItem()); } } LOGGER.debug("Number of ContentPlugins = " + contentPlugins.size()); // Execute each ContentPlugin on the content item. If any plugin fails, then // assume the entire transaction fails, rolling back the storage of the content // item in the content repository (if applicable) try { for (final ContentPlugin plugin : contentPlugins) { createResponse = plugin.process(createResponse); } } catch (PluginExecutionException e) { LOGGER.info("Content Plugin processing failed.", e); // If a STORE_AND_PROCESS directive was being done, will need to delete the // stored content item from the content repository (similar to a rollback) if (directive == Directive.STORE_AND_PROCESS) { String contentId = createResponse.getCreatedContentItem().getId(); LOGGER.debug("Doing storage rollback - Deleting content item " + contentId); ContentItem itemToDelete = new IncomingContentItem(contentId, null, null); itemToDelete.setUri(incomingContentItem.getUri()); DeleteRequest deleteRequest = new DeleteRequestImpl(itemToDelete, null); try { this.provider.delete(deleteRequest); } catch (Exception e2) { LOGGER.warn( "Unable to perform delete because no content storage provider is installed or there is a problem with the content storage provider.", e2); } // Re-throw the exception (this will fail the Camel route that may have // started this request) throw new ContentFrameworkException( "Content Plugin processing failed. Did not store item in content repository and did not create catalog entry.\n" + e.getMessage(), e); } else { // Re-throw the exception (this will fail the Camel route that may have // started this request) throw new ContentFrameworkException( "Content Plugin processing failed. Did not create catalog entry.\n" + e .getMessage(), e); } } } LOGGER.trace("EXITING: create"); return createResponse; } @Override public ReadResponse read(ReadRequest readRequest) throws ContentFrameworkException { LOGGER.trace("ENTERING: read"); ReadResponse response = null; try { response = this.provider.read(readRequest); } catch (StorageException e) { throw new ContentFrameworkException(e); } catch (Exception e) { LOGGER.warn("Content Provider error during read", e); throw new ContentFrameworkException( "Unable to perform read because no content storage provider is installed or there is a problem with the content storage provider.", e); } LOGGER.trace("EXITING: read"); return response; } @Override public UpdateResponse update(final UpdateRequest updateRequest, Request.Directive directive) throws ContentFrameworkException { LOGGER.trace("ENTERING: update"); UpdateResponse updateResponse = null; // If directive includes processing and there are no ContentPlugins currently installed to // support processing, then throw an exception. (Do not want to do the STORE and get the // content repository out of sync with the Metadata Catalog.) if ((directive == Directive.PROCESS || directive == Directive.STORE_AND_PROCESS) && ( this.contentPlugins.size() == 0)) { throw new ContentFrameworkException( "Unable to perform " + directive + " because no ContentPlugins are installed."); } ContentItem itemToUpdate = updateRequest.getContentItem(); if (directive == Directive.STORE || directive == Directive.STORE_AND_PROCESS) { // Verify content item exists in content repository before trying to update it try { ReadRequest readRequest = new ReadRequestImpl( updateRequest.getContentItem().getId(), null); this.provider.read(readRequest); } catch (StorageException e) { LOGGER.info("File does not exist, cannot update, doing a create: ", e); throw new ContentFrameworkException( "File does not exist, cannot update, doing a create: ", e); } catch (Exception e) { LOGGER.warn("Content Provider error during update", e); throw new ContentFrameworkException( "Unable to perform update because no content storage provider is installed or there is a problem with the content storage provider.", e); } LOGGER.info("Updating content repository for content item: " + itemToUpdate.getId()); try { updateResponse = this.provider.update(updateRequest); try { LOGGER.debug( "updated item file length = " + updateResponse.getUpdatedContentItem() .getSize()); } catch (IOException ioe) { } } catch (StorageException e) { throw new ContentFrameworkException(e); } catch (Exception e) { LOGGER.warn("Content Provider error during update", e); throw new ContentFrameworkException( "Unable to perform update because no content storage provider is installed or there is a problem with the content storage provider.", e); } } if (directive == Directive.PROCESS || directive == Directive.STORE_AND_PROCESS) { if (directive == Directive.PROCESS) { // No content update occurred to return an UpdateResponse. So need to // instantiate an UpdateResponse with the original UpdateRequest's ContentItem // in it. if (updateResponse == null) { updateResponse = new UpdateResponseImpl(updateRequest, updateRequest.getContentItem()); } } // Execute each ContentPlugin on the content item. If any plugin fails, then // assume the entire transaction fails, rolling back the storage of the content // item in the content repository (if applicable) try { for (final ContentPlugin plugin : contentPlugins) { updateResponse = plugin.process(updateResponse); } } catch (PluginExecutionException e) { LOGGER.info("Content Plugin processing failed.", e); // Re-throw the exception (this will fail the Camel route that may have // started this request) throw new ContentFrameworkException( "Content Plugin processing failed. " + e.getMessage(), e); } } LOGGER.trace("EXITING: update"); return updateResponse; } @Override public DeleteResponse delete(DeleteRequest deleteRequest, Request.Directive directive) throws ContentFrameworkException { LOGGER.trace("ENTERING: delete"); DeleteResponse deleteResponse = null; // If directive includes processing and there are no ContentPlugins currently installed to // support processing, then throw an exception. (Do not want to do the STORE and get the // content repository out of sync with the Metadata Catalog.) if ((directive == Directive.PROCESS || directive == Directive.STORE_AND_PROCESS) && ( this.contentPlugins.size() == 0)) { throw new ContentFrameworkException( "Unable to perform " + directive + " because no ContentPlugins are installed."); } if (directive == Directive.STORE || directive == Directive.STORE_AND_PROCESS) { try { deleteResponse = this.provider.delete(deleteRequest); } catch (StorageException e) { throw new ContentFrameworkException(e); } catch (Exception e) { LOGGER.warn("Content Provider error during delete", e); throw new ContentFrameworkException( "Unable to perform delete because no content storage provider is installed or there is a problem with the content storage provider.", e); } } if (directive == Directive.PROCESS || directive == Directive.STORE_AND_PROCESS) { if (directive == Directive.PROCESS) { // No content deletion occurred to return a DeleteResponse. So need to // instantiate a DeleteResponse with the original DeleteRequest's ContentItem // in it. if (deleteResponse == null) { deleteResponse = new DeleteResponseImpl(deleteRequest, deleteRequest.getContentItem(), false); } } for (final ContentPlugin plugin : contentPlugins) { try { deleteResponse = plugin.process(deleteResponse); } catch (PluginExecutionException e) { LOGGER.info( "Content Plugin processing failed. This is allowable. Skipping to next plugin.", e); } } } LOGGER.trace("EXITING: delete"); return deleteResponse; } }