From 25ab26bf986d2144d13a9ce703ccdcd883b40b97 Mon Sep 17 00:00:00 2001 From: remillet Date: Mon, 1 Jun 2015 15:07:48 -0700 Subject: [PATCH] CSPACE-6679: Added a Nuxeo listener to delete the original image file from media sourced from an external URL. --- .../resources/OSGI-INF/core-types-contrib.xml | 15 ++- .../nuxeo/nuxeo-platform-listener/build.xml | 3 + .../nuxeo/nuxeo-platform-listener/pom.xml | 1 + .../updateimagederivatives/build.properties | 1 + .../updateimagederivatives/build.xml | 104 +++++++++++++++ .../updateimagederivatives/pom.xml | 61 +++++++++ .../listener/UpdateImageDerivatives.java | 125 ++++++++++++++++++ .../src/main/resources/META-INF/MANIFEST.MF | 16 +++ .../resources/OSGI-INF/core-types-contrib.xml | 4 + .../OSGI-INF/default-life-cycle-contrib.xml | 4 + .../OSGI-INF/deployment-fragment.xml | 10 ++ .../resources/OSGI-INF/ecm-types-contrib.xml | 11 ++ .../resources/OSGI-INF/layouts-contrib.xml | 4 + .../services/blob/BlobResource.java | 5 +- .../blob/nuxeo/BlobDocumentModelHandler.java | 4 +- services/citation/.gitignore | 1 + services/citation/3rdparty/.gitignore | 1 + .../services/common/api/CommonAPI.java | 11 +- .../common/imaging/nuxeo/NuxeoBlobUtils.java | 69 ++++++---- .../client/java/DocumentModelHandler.java | 6 +- .../services/nuxeo/util/NuxeoUtils.java | 100 ++++++++++++-- 21 files changed, 505 insertions(+), 51 deletions(-) create mode 100644 3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/build.properties create mode 100644 3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/build.xml create mode 100644 3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/pom.xml create mode 100644 3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/java/org/collectionspace/services/listener/UpdateImageDerivatives.java create mode 100644 3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/META-INF/MANIFEST.MF create mode 100644 3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/core-types-contrib.xml create mode 100644 3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/default-life-cycle-contrib.xml create mode 100644 3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/deployment-fragment.xml create mode 100644 3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/ecm-types-contrib.xml create mode 100644 3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/layouts-contrib.xml create mode 100644 services/citation/.gitignore create mode 100644 services/citation/3rdparty/.gitignore diff --git a/3rdparty/nuxeo/nuxeo-platform-collectionspace/src/main/resources/OSGI-INF/core-types-contrib.xml b/3rdparty/nuxeo/nuxeo-platform-collectionspace/src/main/resources/OSGI-INF/core-types-contrib.xml index 7ef7f07d6..a9c9cbcd7 100644 --- a/3rdparty/nuxeo/nuxeo-platform-collectionspace/src/main/resources/OSGI-INF/core-types-contrib.xml +++ b/3rdparty/nuxeo/nuxeo-platform-collectionspace/src/main/resources/OSGI-INF/core-types-contrib.xml @@ -1,21 +1,24 @@ - + + + + + + - + - + + diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/build.xml b/3rdparty/nuxeo/nuxeo-platform-listener/build.xml index 7a0e8b1c4..c18b2b0db 100644 --- a/3rdparty/nuxeo/nuxeo-platform-listener/build.xml +++ b/3rdparty/nuxeo/nuxeo-platform-listener/build.xml @@ -117,18 +117,21 @@ description="deploy nuxeo server libs to ${jee.server.cspace}"> + + + diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/pom.xml b/3rdparty/nuxeo/nuxeo-platform-listener/pom.xml index 0ca65fb73..ff686aaf6 100644 --- a/3rdparty/nuxeo/nuxeo-platform-listener/pom.xml +++ b/3rdparty/nuxeo/nuxeo-platform-listener/pom.xml @@ -15,6 +15,7 @@ updateobjectlocationonmove updaterelationsondelete + updateimagederivatives diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/build.properties b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/build.properties new file mode 100644 index 000000000..f4b41625a --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/build.properties @@ -0,0 +1 @@ +listener.module.name=updateimagederivatives diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/build.xml b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/build.xml new file mode 100644 index 000000000..337cca22b --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/build.xml @@ -0,0 +1,104 @@ + + + CollectionSpace Nuxeo listener component type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/pom.xml b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/pom.xml new file mode 100644 index 000000000..4a490c54e --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + org.collectionspace.services.3rdparty.nuxeo.listener + org.collectionspace.services + 4.2-SNAPSHOT + + + UTF-8 + + org.collectionspace.services.listener.updateimagederivatives + org.collectionspace.services.listener.updateimagederivatives + http://maven.apache.org + + + + + org.collectionspace.services + org.collectionspace.services.common + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.movement.service + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.client + ${project.version} + + + + + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-jar-plugin + + + src/main/resources/META-INF/MANIFEST.MF + + ${eclipseVersion} + 2 + + + + + + + + diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/java/org/collectionspace/services/listener/UpdateImageDerivatives.java b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/java/org/collectionspace/services/listener/UpdateImageDerivatives.java new file mode 100644 index 000000000..b73f7cc29 --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/java/org/collectionspace/services/listener/UpdateImageDerivatives.java @@ -0,0 +1,125 @@ +package org.collectionspace.services.listener; + +import java.io.Serializable; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.collectionspace.services.common.api.CommonAPI; +import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface; +import org.collectionspace.services.nuxeo.client.java.CoreSessionWrapper; +import org.collectionspace.services.nuxeo.util.NuxeoUtils; +import org.nuxeo.ecm.core.api.Blob; +import org.nuxeo.ecm.core.api.ClientException; +import org.nuxeo.ecm.core.api.DocumentModel; +import org.nuxeo.ecm.core.api.blobholder.BlobHolder; +import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder; +//import org.nuxeo.ecm.core.api.event.DocumentEventTypes; +import org.nuxeo.ecm.core.event.Event; +import org.nuxeo.ecm.core.event.EventContext; +import org.nuxeo.ecm.core.event.EventListener; +import org.nuxeo.ecm.core.event.impl.DocumentEventContext; +//import org.nuxeo.ecm.platform.picture.api.ImagingDocumentConstants; +import org.nuxeo.ecm.platform.picture.api.ImagingDocumentConstants; + +public class UpdateImageDerivatives implements EventListener { + + // All Nuxeo sessions that get passed around to CollectionSpace code need to + // be wrapped inside of a CoreSessionWrapper. For example: + // CoreSessionInterface coreSession = new + // CoreSessionWrapper(docEventContext.getCoreSession()); + + // FIXME: We might experiment here with using log4j instead of Apache + // Commons Logging; am using the latter to follow Ray's pattern for now + private final static Log logger = LogFactory.getLog(UpdateImageDerivatives.class); + + @Override + public void handleEvent(Event event) throws ClientException { + if (logger.isTraceEnabled()) { + logger.trace(String.format("Entering handleEvent in '%s'...", getClass().getName())); + } + + if (shouldProcessEvent(event) == true) { + DocumentEventContext docEventContext = (DocumentEventContext) event.getContext(); + DocumentModel docModel = docEventContext.getSourceDocument(); + + String eventType = event.getName(); + if (logger.isTraceEnabled()) { + logger.trace(String.format("A(n) '%s' event was received by the %s event listener.", + eventType, getClass().getName())); + //logg + } + + String source = (String)docModel.getProperty(CommonAPI.NUXEO_DUBLINCORE_SCHEMANAME, + CommonAPI.NUXEO_DUBLINCORE_SOURCE); + + if (source != null && source.equalsIgnoreCase(CommonAPI.URL_SOURCED_PICTURE)) { + CoreSessionInterface nuxeoSession = new CoreSessionWrapper(docEventContext.getCoreSession()); + purgeOriginalImage(docModel, nuxeoSession); + nuxeoSession.save(); + } else { + if (logger.isTraceEnabled()) { + logger.trace(String.format("The Nuxeo document titled '%s' did not need processing by the '%s' Nuxeo listener.", + docModel.getTitle(), getClass().getName())); + } + } + } + + if (logger.isTraceEnabled()) { + logger.trace(String.format("Exiting handleEvent in '%s'.", getClass().getName())); + } + } + + private void purgeOriginalImage(DocumentModel docModel, CoreSessionInterface nuxeoSession) { + // + // Empty the document model's "content" property -this does not delete the actual file/blob it + // just disassociates the blob content (aka, the orginal image) from the document. + // + docModel.setPropertyValue("file:content", (Serializable) null); + + // + // Removing this facet ensures the original derivatives are unchanged when + // we call the save method. If we didn't remove the face, then all the + // image derivatives would be disassociated with the document. We want to keep + // the derivatives. + // + NuxeoUtils.removeFacet(docModel, ImagingDocumentConstants.PICTURE_FACET); + nuxeoSession.saveDocument(docModel); // persist the disassociation of the original blob/image + // + // Now that we've emptied the document model's content field, we can add back the Picture facet so + // Nuxeo will still tread this document as a Picture document. + // + NuxeoUtils.addFacet(docModel, ImagingDocumentConstants.PICTURE_FACET); + + // + // Finally, we need to remove the actual blob/image bits that are store on disk. + // + DocumentBlobHolder docBlobHolder = (DocumentBlobHolder) docModel.getAdapter(BlobHolder.class); + Blob blob = docBlobHolder.getBlob(); + if (blob == null) { + logger.error(String.format("Could not get blob for original image. Trying to delete original for: '%s'", + docModel.getTitle())); + } else { + Thread thread = NuxeoUtils.deleteFileOfBlobAsync(blob); + logger.debug(String.format("Started thread '%s' to delete file of blob '%s'.", + thread.getId(), blob.getFilename())); + } + + if (logger.isTraceEnabled()) { + logger.trace(String.format("Exiting handleEvent in '%s'.", getClass().getName())); + } + } + + private boolean shouldProcessEvent(Event event) { + boolean result = false; + + EventContext eventContext = event.getContext(); + if (eventContext != null) { + if (eventContext instanceof DocumentEventContext) { + result = true; + } + } + + return result; + } + +} \ No newline at end of file diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/META-INF/MANIFEST.MF b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 000000000..3b949c555 --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,16 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 1 +Bundle-Name: org.collectionspace.services.listener.updateimagederivatives +Bundle-SymbolicName: org.collectionspace.services.listener.updateimagederivatives;singleton:=true +Bundle-Version: 1.0.0 +Bundle-Localization: plugin +Bundle-Vendor: Nuxeo +Require-Bundle: org.nuxeo.runtime, + org.nuxeo.ecm.core.api, + org.nuxeo.ecm.core, + org.nuxeo.ecm.webapp.core +Provide-Package: org.collectionspace.services.listener.updateimagederivatives +Nuxeo-Component: OSGI-INF/core-types-contrib.xml, + OSGI-INF/default-life-cycle-contrib.xml, + OSGI-INF/ecm-types-contrib.xml, + OSGI-INF/layouts-contrib.xml diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/core-types-contrib.xml b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/core-types-contrib.xml new file mode 100644 index 000000000..0ed276036 --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/core-types-contrib.xml @@ -0,0 +1,4 @@ + + + + diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/default-life-cycle-contrib.xml b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/default-life-cycle-contrib.xml new file mode 100644 index 000000000..6c33c2a12 --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/default-life-cycle-contrib.xml @@ -0,0 +1,4 @@ + + + + diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/deployment-fragment.xml b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/deployment-fragment.xml new file mode 100644 index 000000000..270abbd5f --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/deployment-fragment.xml @@ -0,0 +1,10 @@ + + + + + + ${bundle.fileName} + + + + diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/ecm-types-contrib.xml b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/ecm-types-contrib.xml new file mode 100644 index 000000000..2d650fee6 --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/ecm-types-contrib.xml @@ -0,0 +1,11 @@ + + + + + pictureViewsGenerationDone + + + diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/layouts-contrib.xml b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/layouts-contrib.xml new file mode 100644 index 000000000..e7bab8446 --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateimagederivatives/src/main/resources/OSGI-INF/layouts-contrib.xml @@ -0,0 +1,4 @@ + + + + diff --git a/services/blob/service/src/main/java/org/collectionspace/services/blob/BlobResource.java b/services/blob/service/src/main/java/org/collectionspace/services/blob/BlobResource.java index be4121244..185f4b32d 100644 --- a/services/blob/service/src/main/java/org/collectionspace/services/blob/BlobResource.java +++ b/services/blob/service/src/main/java/org/collectionspace/services/blob/BlobResource.java @@ -145,9 +145,10 @@ public class BlobResource extends NuxeoBasedResource { } if (result == null) { + String errMsg = String.format("Index failed. Could not get the contents for the Blob with CSID = '%s'.", + csid); Response response = Response.status( - Response.Status.INTERNAL_SERVER_ERROR).entity( - "Index failed. Could not get the contents for the Blob.").type("text/plain").build(); + Response.Status.INTERNAL_SERVER_ERROR).entity(errMsg).type("text/plain").build(); throw new CSWebApplicationException(response); } diff --git a/services/blob/service/src/main/java/org/collectionspace/services/blob/nuxeo/BlobDocumentModelHandler.java b/services/blob/service/src/main/java/org/collectionspace/services/blob/nuxeo/BlobDocumentModelHandler.java index 679dce1b8..63162ed3e 100644 --- a/services/blob/service/src/main/java/org/collectionspace/services/blob/nuxeo/BlobDocumentModelHandler.java +++ b/services/blob/service/src/main/java/org/collectionspace/services/blob/nuxeo/BlobDocumentModelHandler.java @@ -166,9 +166,7 @@ extends NuxeoDocumentModelHandler { if (blobOutput != null) { blobInput.setContentStream(blobOutput.getBlobInputStream()); } else { - // If we can't find the blob's content, we'll return a "missing document" image - blobInput.setContentStream(NuxeoBlobUtils.getResource(NuxeoBlobUtils.DOCUMENT_MISSING_PLACEHOLDER_IMAGE)); - mimeTypeBuffer.append(NuxeoBlobUtils.MIME_JPEG); + blobInput.setContentStream(null); } } diff --git a/services/citation/.gitignore b/services/citation/.gitignore new file mode 100644 index 000000000..ae3c17260 --- /dev/null +++ b/services/citation/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/services/citation/3rdparty/.gitignore b/services/citation/3rdparty/.gitignore new file mode 100644 index 000000000..ae3c17260 --- /dev/null +++ b/services/citation/3rdparty/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/services/common-api/src/main/java/org/collectionspace/services/common/api/CommonAPI.java b/services/common-api/src/main/java/org/collectionspace/services/common/api/CommonAPI.java index 0e8819ff5..263fe5efe 100644 --- a/services/common-api/src/main/java/org/collectionspace/services/common/api/CommonAPI.java +++ b/services/common-api/src/main/java/org/collectionspace/services/common/api/CommonAPI.java @@ -18,6 +18,15 @@ public class CommonAPI { public static final String GENERATE_BUNDLES = "core"; public static final String GENERATE_BINDINGS = "delta"; - + + // + // A Nuxeo facet that let's us know whether or not the image/picture was sourced + // from an external URL. + // + public static final String URL_SOURCED_PICTURE = "URLSourcedPicture"; + public static final String NUXEO_DUBLINCORE_SCHEMANAME = "dublincore"; + public static final String NUXEO_DUBLINCORE_TITLE = "title"; + public static final String NUXEO_DUBLINCORE_SOURCE = "source"; + } diff --git a/services/common/src/main/java/org/collectionspace/services/common/imaging/nuxeo/NuxeoBlobUtils.java b/services/common/src/main/java/org/collectionspace/services/common/imaging/nuxeo/NuxeoBlobUtils.java index 2a537c257..0ff0988c4 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/imaging/nuxeo/NuxeoBlobUtils.java +++ b/services/common/src/main/java/org/collectionspace/services/common/imaging/nuxeo/NuxeoBlobUtils.java @@ -38,7 +38,6 @@ import java.util.List; import java.util.Map; import org.nuxeo.runtime.api.Framework; - import org.nuxeo.ecm.platform.picture.api.ImageInfo; import org.nuxeo.ecm.platform.picture.api.ImagingDocumentConstants; import org.nuxeo.ecm.platform.picture.api.ImagingService; @@ -51,7 +50,6 @@ import org.nuxeo.ecm.platform.filemanager.service.FileManagerService; import org.nuxeo.ecm.platform.filemanager.service.extension.FileImporter; import org.nuxeo.ecm.platform.filemanager.utils.FileManagerUtils; import org.nuxeo.ecm.platform.types.TypeManager; - import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.IdRef; import org.nuxeo.ecm.core.api.blobholder.BlobHolder; @@ -62,12 +60,10 @@ import org.nuxeo.ecm.core.api.Blob; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentRef; - +import org.nuxeo.ecm.core.api.VersioningOption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.apache.commons.io.IOUtils; - import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.common.FileUtils; @@ -76,6 +72,7 @@ import org.collectionspace.services.common.blob.BlobInput; import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.TransactionException; import org.collectionspace.services.common.repository.RepositoryClient; +import org.collectionspace.services.common.api.CommonAPI; import org.collectionspace.services.common.api.GregorianCalendarDateTimeUtils; import org.collectionspace.services.common.blob.BlobOutput; import org.collectionspace.services.blob.BlobsCommon; @@ -106,6 +103,8 @@ public class NuxeoBlobUtils { // private static final int MAX_IMAGE_BUFFER = 256 * 1024; // REM: 11/26/2013 - This should be set in a config/property file. + + // // File name constants // @@ -607,7 +606,7 @@ public class NuxeoBlobUtils { // image derivatives. // result = getFileManager().createDocumentFromBlob( - repoSession.getCoreSession(), inputStreamBlob, blobLocation, true, blobName); + repoSession.getCoreSession(), inputStreamBlob, blobLocation, overwrite, blobName); } else { // // User Nuxeo's default file importer/adapter explicitly. This avoids specialized functionality from happening like @@ -620,7 +619,7 @@ public class NuxeoBlobUtils { FileImporter defaultFileImporter = fileManagerService.getPluginByName("DefaultFileImporter"); result = defaultFileImporter.create( - repoSession.getCoreSession(), inputStreamBlob, blobLocation, true, blobName, getTypeService()); + repoSession.getCoreSession(), inputStreamBlob, blobLocation, overwrite, blobName, getTypeService()); } return result; @@ -652,7 +651,7 @@ public class NuxeoBlobUtils { repoSession, inputStreamBlob, blobLocation.getPathAsString(), - true, + false, blobName, useNuxeoAdaptors); result = createBlobsCommon(documentModel, inputStreamBlob); // Now create the metadata about the Nuxeo blob document @@ -778,42 +777,62 @@ public class NuxeoBlobUtils { try { Blob fileBlob = createNuxeoFileBasedBlob(file); - DocumentModel documentModel = createDocumentFromBlob( nuxeoSession, fileBlob, blobLocation.getPathAsString(), - true, + false, file.getName(), useNuxeoAdaptors); result = createBlobsCommon(documentModel, fileBlob); // Now create our metadata resource document - // If the sender wanted us to generate only derivatives, we need to purge/clear the original contents + // If the requester wants us to generate only derivatives, we need to purge/clear the original image file if (purgeOriginal == true && isPurgeAllowed(documentModel) == true) { + // Empty the document model's "content" property -this does not delete the actual file/blob - documentModel.setPropertyValue("file:content", (Serializable) null); + //documentModel.setPropertyValue("file:content", (Serializable) null); if (documentModel.hasFacet(ImagingDocumentConstants.PICTURE_FACET)) { + // + // We're going to use the "source" property field of the Dublin Core schema as a way of indicating to + // our event listener (See UpdateImageDerivatives.java) that the original image needs to be + // purged. The "source" property does not seem to be used by Nuxeo for Picture documents as of v6.0. However, this might + // break in future releases of Nuxeo, so we'll emit a warning to the logs if we find a value in this + // property. + // + String source = (String)documentModel.getProperty(CommonAPI.NUXEO_DUBLINCORE_SCHEMANAME, + CommonAPI.NUXEO_DUBLINCORE_SOURCE); + if (source != null) { + logger.warn(String.format("The Nuxeo dublin core property '%s' is set to '%s'. We expected it to be empty. See JIRA issue CSPACE-6679 for details.", + CommonAPI.NUXEO_DUBLINCORE_SOURCE, source)); + } + documentModel.setProperty(CommonAPI.NUXEO_DUBLINCORE_SCHEMANAME, + CommonAPI.NUXEO_DUBLINCORE_SOURCE, CommonAPI.URL_SOURCED_PICTURE); + // Now with no content, the derivative listener wants to update the derivatives. So to // prevent the listener, we remove the "Picture" facet from the document - NuxeoUtils.removeFacet(documentModel, ImagingDocumentConstants.PICTURE_FACET); // Removing this facet ensures the original derivatives are unchanged. - nuxeoSession.saveDocument(documentModel); + //NuxeoUtils.removeFacet(documentModel, ImagingDocumentConstants.PICTURE_FACET); // Removing this facet ensures the original derivatives are unchanged. // Now that we've emptied the document model's content field, we can add back the Picture facet - NuxeoUtils.addFacet(documentModel, ImagingDocumentConstants.PICTURE_FACET); + //NuxeoUtils.addFacet(documentModel, ImagingDocumentConstants.PICTURE_FACET); } - nuxeoSession.saveDocument(documentModel); + //nuxeoSession.saveDocument(documentModel); // Next, we need to remove the actual file from Nuxeo's data directory - DocumentBlobHolder docBlobHolder = (DocumentBlobHolder) documentModel - .getAdapter(BlobHolder.class); - Blob blob = docBlobHolder.getBlob(); - if(blob == null) { - logger.error("Could not get blob for original image. Trying to delete original for: {}", - file.getName()); - } else { - boolean deleteSuccess = NuxeoUtils.deleteFileOfBlob(docBlobHolder.getBlob()); - } +// Blob blob = docBlobHolder.getBlob(); +// if(blob == null) { +// logger.error("Could not get blob for original image. Trying to delete original for: {}", +// file.getName()); +// } else { +// boolean deleteSuccess = NuxeoUtils.deleteFileOfBlob(docBlobHolder.getBlob()); +// } } + + // + // Persist/save any changes. + // + nuxeoSession.saveDocument(documentModel); + nuxeoSession.save(); + } catch (Exception e) { result = null; logger.error("Could not create new Nuxeo blob document.", e); //FIXME: REM - This should probably be re-throwing the exception? diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/DocumentModelHandler.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/DocumentModelHandler.java index 14977bac4..b451aaa4b 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/DocumentModelHandler.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/DocumentModelHandler.java @@ -29,13 +29,13 @@ import java.util.List; import javax.ws.rs.core.MultivaluedMap; import org.apache.commons.lang.StringUtils; - import org.collectionspace.services.client.Profiler; import org.collectionspace.services.client.CollectionSpaceClient; import org.collectionspace.services.client.IQueryManager; import org.collectionspace.services.client.IRelationsManager; import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; +import org.collectionspace.services.common.api.CommonAPI; import org.collectionspace.services.common.api.GregorianCalendarDateTimeUtils; import org.collectionspace.services.common.api.RefName; import org.collectionspace.services.common.api.RefName.RefNameInterface; @@ -59,14 +59,12 @@ import org.collectionspace.services.lifecycle.StateList; import org.collectionspace.services.lifecycle.TransitionDef; import org.collectionspace.services.lifecycle.TransitionDefList; import org.collectionspace.services.lifecycle.TransitionList; - import org.nuxeo.ecm.core.NXCore; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; import org.nuxeo.ecm.core.api.model.PropertyException; import org.nuxeo.ecm.core.lifecycle.LifeCycleService; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -358,7 +356,7 @@ public abstract class DocumentModelHandler // Nuxeo webapp. // try { - documentModel.setProperty("dublincore", "title", + documentModel.setProperty(CommonAPI.NUXEO_DUBLINCORE_SCHEMANAME, CommonAPI.NUXEO_DUBLINCORE_TITLE, documentModel.getName()); } catch (Exception x) { if (logger.isWarnEnabled() == true) { diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/util/NuxeoUtils.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/util/NuxeoUtils.java index 15da22d1c..205a0ec4f 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/util/NuxeoUtils.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/util/NuxeoUtils.java @@ -42,10 +42,9 @@ import org.collectionspace.services.common.document.DocumentUtils; import org.collectionspace.services.common.query.QueryContext; import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentException; import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface; - import org.dom4j.Document; import org.dom4j.io.SAXReader; - +import org.mortbay.log.Log; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; import org.nuxeo.ecm.core.api.ClientException; @@ -54,6 +53,7 @@ import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentRef; import org.nuxeo.ecm.core.api.IdRef; import org.nuxeo.ecm.core.api.PathRef; +import org.nuxeo.ecm.core.api.impl.blob.BlobWrapper; import org.nuxeo.ecm.core.api.model.PropertyException; import org.nuxeo.ecm.core.io.DocumentPipe; import org.nuxeo.ecm.core.io.DocumentReader; @@ -62,10 +62,10 @@ import org.nuxeo.ecm.core.io.impl.DocumentPipeImpl; import org.nuxeo.ecm.core.io.impl.plugins.SingleDocumentReader; import org.nuxeo.ecm.core.io.impl.plugins.XMLDocumentWriter; import org.nuxeo.ecm.core.schema.SchemaManager; +import org.nuxeo.ecm.core.storage.StorageBlob; import org.nuxeo.ecm.core.storage.binary.Binary; import org.nuxeo.ecm.core.storage.sql.coremodel.SQLBlob; import org.nuxeo.runtime.api.Framework; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,15 +92,31 @@ public class NuxeoUtils { private static final String ORDER_BY_CLAUSE_REGEX = "\\w+(_\\w+)?:\\w+(/(\\*|\\w+))*( ASC| DESC)?(, \\w+(_\\w+)?:\\w+(/(\\*|\\w+))*( ASC| DESC)?)*"; /* - * Keep this method private. This method uses reflection to gain access to a protected field in Nuxeo's "Binary" class. Once we learn how + * Keep this method private. This method uses reflection to gain access to a protected field in Nuxeo's "Binary" class. If and when we learn how * to locate the "file" field of a Binary instance without breaking our "contract" with this class, we should minimize * our use of this method. */ private static File getFileOfBlob(Blob blob) { File result = null; - if (blob instanceof SQLBlob) { - SQLBlob sqlBlob = (SQLBlob)blob; + if (blob instanceof BlobWrapper) { + BlobWrapper blobWrapper = (BlobWrapper)blob; + try { + Field blobField; + blobField = blobWrapper.getClass().getDeclaredField("blob"); + boolean accessibleState = blobField.isAccessible(); + if (accessibleState == false) { + blobField.setAccessible(true); + } + blob = (StorageBlob)blobField.get(blobWrapper); + blobField.setAccessible(accessibleState); // set it back to its original access state + } catch (Exception e) { + logger.error("blob field of BlobWrapper is not accessible.", e); + } + } + + if (blob instanceof StorageBlob) { + StorageBlob sqlBlob = (StorageBlob)blob; Binary binary = sqlBlob.getBinary(); try { Field fileField = binary.getClass().getDeclaredField("file"); @@ -118,13 +134,77 @@ public class NuxeoUtils { return result; } - static public boolean deleteFileOfBlob(Blob blob) { - boolean result = false; + static public Thread deleteFileOfBlobAsync(Blob blob) { + Thread result = null; + + // + // Define a new thread that will try to delete the file of the blob. We + // need this to happen on a separate thread because our current thread seems + // to still have an active handle to the file so our non-thread delete calls + // are failing. The new thread will make 10 attempts, separated by 1 second, to + // delete the file. If after 10 attempts, it still can't delete the file, it will + // log an error. + // + final File fileToDelete = getFileOfBlob(blob); + final String blobName = blob.getFilename(); + Thread deleteFileThread = new Thread() { + @Override public void run() { + boolean deleteSuccess = false; + int attempts = 0; + while (attempts++ < 10 && deleteSuccess != true) { + deleteSuccess = deleteFile(fileToDelete); + if (deleteSuccess == false) { + // + // We couldn't delete the file, so some other thread might still + // have a handle to it. Let's put this thread to sleep for 1 second + // before trying to delete it again. + // + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error(String.format("Unable to delete file '%s' of blob '%s'.", + fileToDelete.getAbsoluteFile(), blobName), e); + } + } + } + // + // Now log the result. + // + if (deleteSuccess) { + logger.debug(String.format("Successfully deleted file '%s' of blob '%s'.", + fileToDelete.getAbsoluteFile(), blobName)); + } else { + logger.error(String.format("Unable to delete file '%s' of blob '%s'.", + fileToDelete.getAbsoluteFile(), blobName)); + } + } + }; + deleteFileThread.start(); + result = deleteFileThread; + return result; + } + + static public boolean deleteFileOfBlob(Blob blob) { File fileToDelete = getFileOfBlob(blob); - result = fileToDelete.delete(); + return deleteFile(fileToDelete); + } + + static public boolean deleteFile(File fileToDelete) { + boolean result = true; + + Exception deleteException = null; + try { + java.nio.file.Files.delete(fileToDelete.toPath()); + Log.debug(String.format("Deleted file '%s'.", fileToDelete.getCanonicalPath())); + } catch (IOException e) { + deleteException = e; + result = false; + } + if (result == false) { - logger.warn("Could not delete the blob file at: " + fileToDelete.getAbsolutePath()); + logger.warn("Could not delete the file at: " + fileToDelete.getAbsolutePath(), + deleteException); } return result; -- 2.47.3