--- /dev/null
+listener.module.name=updateobjectlocationonmove
--- /dev/null
+<project name="org.collectionspace.services.3rdparty.nuxeo.listener.updateobjectlocationonmove">
+ <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="" />
+ <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>3.2-SNAPSHOT</version>
+ </parent>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <artifactId>org.collectionspace.services.listener.updateobjectlocationonmove</artifactId>
+ <name>org.collectionspace.services.listener.updateobjectlocationonmove</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>
+ </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.util.ArrayList;
+import java.util.List;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuxeo.ecm.core.api.ClientException;
+import org.nuxeo.ecm.core.api.CoreSession;
+import org.nuxeo.ecm.core.api.DocumentModel;
+import org.nuxeo.ecm.core.api.DocumentModelList;
+import org.nuxeo.ecm.core.api.impl.LifeCycleFilter;
+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;
+
+public class UpdateRelationsOnDelete implements EventListener {
+
+ // FIXME: Consider adding the following constant to
+ // org.collectionspace.services.common.workflow.jaxb.WorkflowJAXBSchema
+ // and referencing it from there.
+ private static final String WORKFLOWTRANSITION_TO = "to";
+ // FIXME: Consider substituting existing constant WorkflowClient.WORKFLOWSTATE_DELETED
+ private static final String WORKFLOWSTATE_DELETED = "deleted";
+ // FIXME: Consider substituting existing constant WorkflowClient.WORKFLOWSTATE_LOCKED
+ private static final String WORKFLOWSTATE_LOCKED = "locked";
+ // FIXME: Consider substituting existing constant WorkflowClient.WORKFLOWTRANSITION_DELETE
+ private static final String WORKFLOWTRANSITION_DELETE = "delete";
+
+ // FIXME: We might experiment here with using log4j instead of Apache Commons Logging;
+ // am using the latter to follow Ray's pattern for now
+ final Log logger = LogFactory.getLog(UpdateRelationsOnDelete.class);
+
+ @Override
+ public void handleEvent(Event event) throws ClientException {
+ logger.info("In handleEvent in UpdateRelationsOnDelete ...");
+
+ EventContext eventContext = event.getContext();
+
+ if (isDocumentSoftDeletedEvent(eventContext)) {
+
+ DocumentEventContext docContext = (DocumentEventContext) eventContext;
+ DocumentModel docModel = docContext.getSourceDocument();
+
+ // Retrieve a list of relation records, where the soft deleted
+ // document provided in the context of the current event is
+ // either the subject or object of any relation
+
+ // Build a query string
+ String csid = docModel.getName();
+ StringBuilder queryString = new StringBuilder("");
+ queryString.append("SELECT * FROM Relation WHERE ");
+ // FIXME: Obtain and add tenant ID to the query here
+ // queryString.append("collectionspace_core:tenantId = 1 ");
+ // queryString.append(" AND ");
+ // queryString.append("ecm:currentLifeCycleState <> 'deleted' ");
+ queryString.append("ecm:isProxy = 0 ");
+ queryString.append(" AND ");
+ queryString.append("(");
+ queryString.append("relations_common:subjectCsid = ");
+ queryString.append("'");
+ queryString.append(csid);
+ queryString.append("'");
+ queryString.append(" OR ");
+ queryString.append("relations_common:objectCsid = ");
+ queryString.append("'");
+ queryString.append(csid);
+ queryString.append("'");
+ queryString.append(")");
+
+ // Create a filter to exclude from the list results any records
+ // that have already been soft deleted or are locked
+ List<String> workflowStatesToFilter = new ArrayList<String>();
+ workflowStatesToFilter.add(WORKFLOWSTATE_DELETED);
+ workflowStatesToFilter.add(WORKFLOWSTATE_LOCKED);
+ LifeCycleFilter workflowStateFilter = new LifeCycleFilter(null, workflowStatesToFilter);
+
+ // Perform the filtered query
+ CoreSession session = docModel.getCoreSession();
+ DocumentModelList matchingDocuments;
+ try {
+ matchingDocuments = session.query(queryString.toString(), workflowStateFilter);
+ } catch (ClientException ce) {
+ logger.warn("Error attempting to retrieve relation records where "
+ + "record of type '" + docModel.getType() + "' with CSID " + csid
+ + " is the subject or object of any relation: " + ce.getMessage());
+ throw ce;
+ }
+
+ // Cycle through the list results, soft deleting each matching relation record
+ logger.trace("Attempting to soft delete " + matchingDocuments.size() + " relation records.");
+ for (DocumentModel doc : matchingDocuments) {
+ doc.followTransition(WORKFLOWTRANSITION_DELETE);
+ }
+
+ }
+
+ }
+
+ /**
+ * Identifies whether a supplied event concerns a document that has
+ * been transitioned to the 'deleted' workflow state.
+ *
+ * @param eventContext an event context
+ *
+ * @return true if this event concerns a document that has
+ * been transitioned to the 'deleted' workflow state.
+ */
+ private boolean isDocumentSoftDeletedEvent(EventContext eventContext) {
+ boolean isSoftDeletedEvent = false;
+ if (eventContext instanceof DocumentEventContext) {
+ if (eventContext.getProperties().containsKey(WORKFLOWTRANSITION_TO)
+ && eventContext.getProperties().get(WORKFLOWTRANSITION_TO).equals(WORKFLOWSTATE_DELETED)) {
+ isSoftDeletedEvent = true;
+ }
+ }
+ return isSoftDeletedEvent;
+ }
+}
--- /dev/null
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 1
+Bundle-Name: org.collectionspace.nuxeo.listener.relation
+Bundle-SymbolicName: org.collectionspace.nuxeo.listener.relation;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.nuxeo.listener.relation
+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.nuxeo.listener.relation.core.types">
+ <!-- This page intentionally left blank -->
+</component>
--- /dev/null
+<?xml version="1.0"?>
+<component name="org.collectionspace.nuxeo.listener.relation.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.nuxeo.listener.relation.ecm.types">
+
+ <extension target="org.nuxeo.ecm.core.event.EventServiceComponent" point="listener">
+ <listener name="relationsuborobjdeletionlistener" async="false" postCommit="false"
+ class="org.collectionspace.services.listener.UpdateRelationsOnDelete" priority="999">
+ <event>lifecycle_transition_event</event>
+ </listener>
+ </extension>
+
+</component>
--- /dev/null
+<?xml version="1.0"?>
+<component name="org.collectionspace.nuxeo.listener.relation.layouts.webapp">
+ <!-- This page intentionally left blank -->
+</component>