<?xml version="1.0"?>
<component name="org.collectionspace.shared.core.types">
<!-- A common ancestor for all CollectionSpace document types -->
- <extension target="org.nuxeo.ecm.core.schema.TypeService"
- point="doctype">
+ <extension target="org.nuxeo.ecm.core.schema.TypeService" point="doctype">
<doctype name="CollectionSpaceDocument" extends="Document">
<schema name="common" />
<schema name="dublincore" />
<schema name="collectionspace_core" />
</doctype>
+
+ <!-- A facet for marking blobs/images that came from extern URLs -->
+ <facet name="URLSourcedPicture">
+ </facet>
</extension>
+
<!-- A schema def and doctype def for the "Subitem" document type. -->
- <extension target="org.nuxeo.ecm.core.schema.TypeService"
- point="schema">
+ <extension target="org.nuxeo.ecm.core.schema.TypeService" point="schema">
<schema name="subitem" prefix="subitem" src="schemas/subitem.xsd" />
</extension>
- <extension target="org.nuxeo.ecm.core.schema.TypeService"
- point="doctype">
+
+ <extension target="org.nuxeo.ecm.core.schema.TypeService" point="doctype">
<doctype name="Subitem" extends="CollectionSpaceDocument">
<schema name="common" />
<schema name="dublincore" />
description="deploy nuxeo server libs to ${jee.server.cspace}">
<ant antfile="updateobjectlocationonmove/build.xml" target="deploy" inheritall="false"/>
<ant antfile="updaterelationsondelete/build.xml" target="deploy" inheritall="false"/>
+ <ant antfile="updateimagederivatives/build.xml" target="deploy" inheritall="false"/>
</target>
<target name="undeploy"
description="undeploy nuxeo server libs from ${jee.server.cspace}">
<ant antfile="updateobjectlocationonmove/build.xml" target="undeploy" inheritall="false"/>
<ant antfile="updaterelationsondelete/build.xml" target="undeploy" inheritall="false"/>
+ <ant antfile="updateimagederivatives/build.xml" target="undeploy" inheritall="false"/>
</target>
<target name="dist"
description="generate distribution for nuxeo server libs" depends="package">
<ant antfile="updateobjectlocationonmove/build.xml" target="dist" inheritall="false"/>
<ant antfile="updaterelationsondelete/build.xml" target="dist" inheritall="false"/>
+ <ant antfile="updateimagederivatives/build.xml" target="dist" inheritall="false"/>
</target>
</project>
<modules>
<module>updateobjectlocationonmove</module>
<module>updaterelationsondelete</module>
+ <module>updateimagederivatives</module>
</modules>
<dependencies>
--- /dev/null
+listener.module.name=updateimagederivatives
--- /dev/null
+<project name="org.collectionspace.services.3rdparty.nuxeo.listener.updateimagederivatives">
+ <description>
+ CollectionSpace Nuxeo listener component type
+ </description>
+ <!-- Set global properties for this build -->
+ <property name="services.trunk" value="../../../.."/>
+ <!-- Environment should be declared before reading build.properties -->
+ <property environment="env" />
+ <!-- Set global properties for this build -->
+ <property file="${services.trunk}/build.properties" />
+ <!-- Set local properties for this build -->
+ <property file="build.properties" />
+ <property name="mvn.opts" value="-V" />
+ <property name="src" location="src"/>
+ <property name="build" location="build"/>
+ <property name="dist" location="dist"/>
+
+ <!-- module.name variable is set in the local properties file -->
+ <property name="jar.name"
+ value="org.collectionspace.services.listener.${listener.module.name}-${cspace.release}.jar"/>
+
+ <property name="jar.all"
+ value="org.collectionspace.services.listener.${listener.module.name}-*.jar"/>
+
+ <condition property="osfamily-unix">
+ <os family="unix" />
+ </condition>
+ <condition property="osfamily-windows">
+ <os family="windows" />
+ </condition>
+
+ <target name="init" >
+ <!-- Create the time stamp -->
+ <tstamp/>
+ <!-- Create the build directory structure used by compile -->
+ <mkdir dir="${build}"/>
+ </target>
+
+ <target name="package" depends="package-unix,package-windows"
+ description="Package CollectionSpace Services" />
+ <target name="package-unix" if="osfamily-unix">
+ <exec executable="mvn" failonerror="true">
+ <arg value="package" />
+ <arg value="-Dmaven.test.skip=true" />
+ <arg value="-f" />
+ <arg value="${basedir}/pom.xml" />
+ <arg value="-N" />
+ <arg value="${mvn.opts}" />
+ </exec>
+ </target>
+ <target name="package-windows" if="osfamily-windows">
+ <exec executable="cmd" failonerror="true">
+ <arg value="/c" />
+ <arg value="mvn.bat" />
+ <arg value="package" />
+ <arg value="-Dmaven.test.skip=true" />
+ <arg value="-f" />
+ <arg value="${basedir}/pom.xml" />
+ <arg value="-N" />
+ <arg value="${mvn.opts}" />
+ </exec>
+ </target>
+
+ <target name="install" depends="install-unix,install-windows"
+ description="Install" />
+ <target name="install-unix" if="osfamily-unix">
+ <exec executable="mvn" failonerror="true">
+ <arg value="install" />
+ <arg value="-Dmaven.test.skip=true" />
+ <arg value="-f" />
+ <arg value="${basedir}/pom.xml" />
+ <arg value="-N" />
+ <arg value="${mvn.opts}" />
+ </exec>
+ </target>
+ <target name="install-windows" if="osfamily-windows">
+ <exec executable="cmd" failonerror="true">
+ <arg value="/c" />
+ <arg value="mvn.bat" />
+ <arg value="install" />
+ <arg value="-Dmaven.test.skip=true" />
+ <arg value="-f" />
+ <arg value="${basedir}/pom.xml" />
+ <arg value="-N" />
+ <arg value="${mvn.opts}" />
+ </exec>
+ </target>
+
+ <target name="deploy" depends="install"
+ description="deploy collectionspace core doctype in ${jee.server.nuxeo}">
+ <copy file="${basedir}/target/${jar.name}"
+ todir="${jee.deploy.nuxeo.plugins}"/>
+ </target>
+
+ <target name="undeploy"
+ description="undeploy collectionspace Thumbnail service from ${jee.server.nuxeo}">
+ <delete>
+ <fileset dir="${jee.deploy.nuxeo.plugins}">
+ <include name="${jar.all}"/>
+ </fileset>
+ </delete>
+ </target>
+
+</project>
--- /dev/null
+<?xml version="1.0"?>
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>org.collectionspace.services.3rdparty.nuxeo.listener</artifactId>
+ <groupId>org.collectionspace.services</groupId>
+ <version>4.2-SNAPSHOT</version>
+ </parent>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <artifactId>org.collectionspace.services.listener.updateimagederivatives</artifactId>
+ <name>org.collectionspace.services.listener.updateimagederivatives</name>
+ <url>http://maven.apache.org</url>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.collectionspace.services</groupId>
+ <artifactId>org.collectionspace.services.common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.collectionspace.services</groupId>
+ <artifactId>org.collectionspace.services.movement.service</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.collectionspace.services</groupId>
+ <artifactId>org.collectionspace.services.client</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestFile> src/main/resources/META-INF/MANIFEST.MF </manifestFile>
+ <manifestEntries>
+ <Bundle-Version>${eclipseVersion}</Bundle-Version>
+ <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+<?xml version="1.0"?>
+<component name="org.collectionspace.services.listener.updateimagederivatives.core.types">
+ <!-- This page intentionally left blank -->
+</component>
--- /dev/null
+<?xml version="1.0"?>
+<component name="org.collectionspace.services.listener.updateimagederivatives.LifeCycle">
+ <!-- This page intentionally left blank -->
+</component>
--- /dev/null
+<?xml version="1.0"?>
+<fragment>
+
+ <extension target="application#MODULE">
+ <module>
+ <java>${bundle.fileName}</java>
+ </module>
+ </extension>
+
+</fragment>
--- /dev/null
+<?xml version="1.0"?>
+<component name="org.collectionspace.services.listener.updateimagederivatives.ecm.types">
+ <extension target="org.nuxeo.ecm.core.event.EventServiceComponent"
+ point="listener">
+ <listener name="updateimagederivativeslistener" async="true"
+ postCommit="true"
+ class="org.collectionspace.services.listener.UpdateImageDerivatives">
+ <event>pictureViewsGenerationDone</event>
+ </listener>
+ </extension>
+</component>
--- /dev/null
+<?xml version="1.0"?>
+<component name="org.collectionspace.services.listener.updateimagederivatives.layouts.webapp">
+ <!-- This page intentionally left blank -->
+</component>
}
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);
}
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);
}
}
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";
+
}
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;
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;
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;
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;
//
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
//
// 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
FileImporter defaultFileImporter = fileManagerService.getPluginByName("DefaultFileImporter");
result = defaultFileImporter.create(
- repoSession.getCoreSession(), inputStreamBlob, blobLocation, true, blobName, getTypeService());
+ repoSession.getCoreSession(), inputStreamBlob, blobLocation, overwrite, blobName, getTypeService());
}
return result;
repoSession,
inputStreamBlob,
blobLocation.getPathAsString(),
- true,
+ false,
blobName,
useNuxeoAdaptors);
result = createBlobsCommon(documentModel, inputStreamBlob); // Now create the metadata about the Nuxeo blob document
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?
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;
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;
// 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) {
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;
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;
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;
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");
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;