<?xml version="1.0"?>
<component name="org.collectionspace.ecm.platform.default.LifeCycleManagerExtensions">
- <extension target="org.nuxeo.ecm.core.lifecycle.LifeCycleService"
- point="types">
- <types>
- <type name="CollectionSpaceDocument">default</type>
- </types>
- </extension>
+ <extension target="org.nuxeo.ecm.core.lifecycle.LifeCycleService" point="types">
+ <types>
+ <type name="CollectionSpaceDocument">default</type>
+ </types>
+ </extension>
+
+ <!-- The definition of the CollectionSpace default lifecycle name "cs_default" -->
+ <extension target="org.nuxeo.ecm.core.lifecycle.LifeCycleService" point="lifecycle">
+ <documentation>CollectionSpace core default life cycle definition.</documentation>
+ <lifecycle name="cs_default" defaultInitial="project">
+ <transitions>
+ <transition name="delete" destinationState="deleted">
+ <description>Move document to trash (temporary delete)</description>
+ </transition>
+ <transition name="undelete" destinationState="project">
+ <description>Undelete the document.</description>
+ </transition>
+ </transitions>
+ <states>
+ <state name="project" description="Default state" initial="true">
+ <transitions>
+ <transition>delete</transition>
+ </transitions>
+ </state>
+ <state name="deleted" description="Document is deleted">
+ <transitions>
+ <transition>undelete</transition>
+ </transitions>
+ </state>
+ </states>
+ </lifecycle>
+ </extension>
+
+ <!-- The definition of the CollectionSpace locking lifecycle name "cs_locking" -->
+ <extension target="org.nuxeo.ecm.core.lifecycle.LifeCycleService" point="lifecycle">
+ <documentation>CollectionSpace "locking" life cycle definition.</documentation>
+ <lifecycle name="cs_locking" defaultInitial="project">
+ <transitions>
+ <transition name="lock" destinationState="locked">
+ <description>Lock document</description>
+ </transition>
+ <transition name="delete" destinationState="deleted">
+ <description>Move document to trash (temporary delete)</description>
+ </transition>
+ <transition name="undelete" destinationState="project">
+ <description>Undelete the document.</description>
+ </transition>
+ </transitions>
+ <states>
+ <state name="project" description="Default state" initial="true">
+ <transitions>
+ <transition>delete</transition>
+ <transition>lock</transition>
+ </transitions>
+ </state>
+ <state name="locked" description="Locked state">
+ <!-- No transitions allowed from locked state. -->
+ </state>
+ <state name="deleted" description="Document is deleted">
+ <transitions>
+ <transition>undelete</transition>
+ </transitions>
+ </state>
+ </states>
+ </lifecycle>
+ </extension>
- <!-- The definition of the CollectionSpace default lifecycle name "cs_default" -->
- <extension target="org.nuxeo.ecm.core.lifecycle.LifeCycleService"
- point="lifecycle">
- <documentation>CollectionSpace core default life cycle definition.</documentation>
- <lifecycle name="cs_default" defaultInitial="project">
- <transitions>
- <transition name="delete" destinationState="deleted">
- <description>Move document to trash (temporary delete)</description>
- </transition>
- <transition name="undelete" destinationState="project">
- <description>Undelete the document.</description>
- </transition>
- </transitions>
- <states>
- <state name="project" description="Default state" initial="true">
- <transitions>
- <transition>delete</transition>
- </transitions>
- </state>
- <state name="deleted" description="Document is deleted">
- <transitions>
- <transition>undelete</transition>
- </transitions>
- </state>
- </states>
- </lifecycle>
- </extension>
-
- <!-- The definition of the CollectionSpace locking lifecycle name "cs_locking" -->
- <extension target="org.nuxeo.ecm.core.lifecycle.LifeCycleService"
- point="lifecycle">
- <documentation>CollectionSpace "locking" life cycle definition.</documentation>
- <lifecycle name="cs_locking" defaultInitial="project">
- <transitions>
- <transition name="lock" destinationState="locked">
- <description>Lock document</description>
- </transition>
- <transition name="delete" destinationState="deleted">
- <description>Move document to trash (temporary delete)</description>
- </transition>
- <transition name="undelete" destinationState="project">
- <description>Undelete the document.</description>
- </transition>
- </transitions>
- <states>
- <state name="project" description="Default state" initial="true">
- <transitions>
- <transition>delete</transition>
- <transition>lock</transition>
- </transitions>
- </state>
- <state name="locked" description="Locked state">
- <!-- No transitions allowed from locked state. -->
- </state>
- <state name="deleted" description="Document is deleted">
- <transitions>
- <transition>undelete</transition>
- </transitions>
- </state>
- </states>
- </lifecycle>
- </extension>
-
- <!-- The definition of the CollectionSpace SAS lifecycle name "cs_sas" for Shared authority resources -->
<!--
- We have four states: "project", "locked", "deleted", and "locked_deleted".
+ The definition of the CollectionSpace "Replication" lifecycle named "cs_replicating" for synchronized resources
+
+ We have 4 states: "project", "replicated", "deleted", and "replicated_deleted".
+ We have 8 transition verbs: "", "", "", "", "", "", "", ""
- We can move from Project <-> (Locked, Delete) <-> LockedAndDeleted
+ Example state changes:
+ - We can move from state "project" -> (via transition "replicate") to state -> "replicated"
+ - We can move from state "project" -> (via transitions "replicate", "delete_replicated") to state -> "replicated_deleted"
+ - We can move from state "project" -> (via transitions "delete", "replicate_deleted") to state -> "replicated_deleted"
+ - We can move from state "project" -> (via transitions "delete") to state -> "deleted"
- Project <-> (Locked, Delete) uses these transitions: lock, unlock, delete, undelete
- LockedAndDeleted <-> (Locked, Deleted) uses these transitions: lock_deleted, unlock_deleted, delete_locked, undelete_locked
+ For more information see this wiki page: https://wiki.collectionspace.org/pages/viewpage.action?pageId=162496564
-->
- <extension target="org.nuxeo.ecm.core.lifecycle.LifeCycleService"
- point="lifecycle">
- <documentation>CollectionSpace "SAS" life cycle definition.</documentation>
- <lifecycle name="cs_sas" defaultInitial="project">
- <transitions>
- <!-- Transitions TO the "project" state -->
- <transition name="unlock" destinationState="project">
- <description>Unlock the document back to project state.</description>
- </transition>
- <transition name="undelete" destinationState="project">
- <description>Undelete the document to the project state.</description>
- </transition>
-
- <!-- Transitions FROM "project" state TO the "locked" and the "deleted" states -->
- <transition name="lock" destinationState="locked">
- <description>Lock a document from the project state</description>
- </transition>
- <transition name="delete" destinationState="deleted">
- <description>Soft-delete the document from the project state</description>
- </transition>
+ <extension target="org.nuxeo.ecm.core.lifecycle.LifeCycleService" point="lifecycle">
+ <documentation>CollectionSpace "cs_replicating" life cycle definition.</documentation>
+ <lifecycle name="cs_replicating" defaultInitial="project">
+ <transitions>
+ <!-- Transitions TO the "project" state -->
+ <transition name="unreplicate" destinationState="project">
+ <description>Unreplicate the document back to project state.</description>
+ </transition>
+ <transition name="undelete" destinationState="project">
+ <description>Undelete the document to the project state.</description>
+ </transition>
+
+ <!-- Transitions FROM "project" state TO the "replicated" and the "deleted" states -->
+ <transition name="replicate" destinationState="replicated">
+ <description>Replicate a document from the project state</description>
+ </transition>
+ <transition name="delete" destinationState="deleted">
+ <description>Soft-delete the document from the project state</description>
+ </transition>
+
+ <!--
+ Notice the convention used in the transition names below. {transition-verb)_{lifecycle-state} -e.g., delete_replicated, replicate_deleted
+ This convention is critical to the code here: org.collectionspace.services.client.AuthorityClient.AuthorityItemDocumentModelHandler
+ -->
+
+ <!-- Transitions TO "replicated_deleted" state -->
+ <transition name="delete_replicated" destinationState="replicated_deleted">
+ <description>Delete the replicated document from the "replicated" state</description>
+ </transition>
+ <transition name="replicate_deleted" destinationState="replicated_deleted">
+ <description>Replicate the deleted document from the "deleted" state.</description>
+ </transition>
+
+ <!-- Transitions FROM "replicated_deleted" state -->
+ <transition name="undelete_replicated" destinationState="replicated">
+ <description>Undelete the replicated document from replicated_deleted state</description>
+ </transition>
+ <transition name="unreplicate_deleted" destinationState="deleted">
+ <description>Unreplicate the deleted document.</description>
+ </transition>
+
+ </transitions>
+
+ <states>
+ <state name="project" description="Default state" initial="true">
+ <transitions>
+ <transition>delete</transition>
+ <!-- To "deleted" state -->
+ <transition>replicate</transition>
+ <!-- To "replicated" state -->
+ </transitions>
+ </state>
+ <state name="replicated" description="Replicated state">
+ <transition>unreplicate</transition>
+ <!-- To "project" state -->
+ <transition>delete_replicated</transition>
+ <!-- To "replicated_deleted" state -->
+ </state>
+ <state name="deleted" description="Document is deleted">
+ <transitions>
+ <transition>undelete</transition>
+ <!-- To "project" state -->
+ <transition>replicate_deleted</transition>
+ <!-- To "replicated_deleted" state -->
+ </transitions>
+ </state>
+ <state name="replicated_deleted" description="Document is replicated and deleted">
+ <transitions>
+ <transition>unreplicate_deleted</transition>
+ <!-- To "deleted" state -->
+ <transition>undelete_replicated</transition>
+ <!-- To "replicated" state -->
+ </transitions>
+ </state>
+ </states>
+ </lifecycle>
+ </extension>
- <!--
- Notice the convention used in the transition names below. {transition-verb)_{lifecycle-state} -e.g., delete_locked
- This convention is critical to the code here: org.collectionspace.services.client.AuthorityClient.AuthorityItemDocumentModelHandler
- -->
-
- <!-- Transitions TO "locked_deleted" state -->
- <transition name="delete_locked" destinationState="locked_deleted">
- <description>Delete the locked document from the "locked" state</description>
- </transition>
- <transition name="lock_deleted" destinationState="locked_deleted">
- <description>Lock the deleted document from the "deleted" state.</description>
- </transition>
-
- <!-- Transitions FROM "locked_deleted" state -->
- <transition name="undelete_locked" destinationState="locked">
- <description>Undelete the locked document from locked_deleted state</description>
- </transition>
- <transition name="unlock_deleted" destinationState="deleted">
- <description>Unlock the deleted document.</description>
- </transition>
-
- </transitions>
-
- <states>
- <state name="project" description="Default state" initial="true">
- <transitions>
- <transition>delete</transition> <!-- To "deleted" state -->
- <transition>lock</transition> <!-- To "locked" state -->
- </transitions>
- </state>
- <state name="locked" description="Locked state">
- <transition>unlock</transition> <!-- To "project" state -->
- <transition>delete_locked</transition> <!-- To "locked_deleted" state -->
- </state>
- <state name="deleted" description="Document is deleted">
- <transitions>
- <transition>undelete</transition> <!-- To "project" state -->
- <transition>lock_deleted</transition> <!-- To "locked_deleted" state -->
- </transitions>
- </state>
- <state name="locked_deleted" description="Document is locked and deleted">
- <transitions>
- <transition>unlock_deleted</transition> <!-- To "deleted" state -->
- <transition>undelete_locked</transition><!-- To "locked" state -->
- </transitions>
- </state>
- </states>
- </lifecycle>
- </extension>
-
</component>
/**
* Identifies whether a document is an active document; currently, whether
- * it is not in the 'deleted' workflow state.
+ * it is not in a 'deleted' workflow state.
*
* @param docModel
* @return true if the document is an active document; false if it is not.
workflowStatesToFilter.add(WorkflowClient.WORKFLOWSTATE_DELETED);
workflowStatesToFilter.add(WorkflowClient.WORKFLOWSTATE_LOCKED);
workflowStatesToFilter.add(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED);
+ workflowStatesToFilter.add(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED);
+
LifeCycleFilter workflowStateFilter = new LifeCycleFilter(null, workflowStatesToFilter);
// Perform the filtered query
}
}
- protected boolean supportsSync(String tenantId, String serviceName) {
+ protected boolean supportsReplicating(String tenantId, String serviceName) {
boolean result = false;
ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
- result = sb.isSupportsSynchronization();
+ result = sb.isSupportsReplicating();
return result;
}
/*
* Make sure this authority service supports synchronization
*/
- if (supportsSync(ctx.getTenantId(), ctx.getServiceName()) == false) {
+ if (supportsReplicating(ctx.getTenantId(), ctx.getServiceName()) == false) {
throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
}
AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
itemIdentifier, parentIdentifier));
}
//
- // Since we're creating an item that was sourced from the SAS, we need to lock it.
+ // Since we're creating an item that was sourced from the replication server, we need to replicate it locally.
//
authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier,
- WorkflowClient.WORKFLOWTRANSITION_LOCK, AuthorityServiceUtils.DONT_UPDATE_REV);
+ WorkflowClient.WORKFLOWTRANSITION_REPLICATE, AuthorityServiceUtils.DONT_UPDATE_REV); // don't update the rev number of the new replicated item (use the rev number of the sourced item)
}
/**
package org.collectionspace.services.common.vocabulary.nuxeo;
import org.collectionspace.services.client.AuthorityClient;
-import org.collectionspace.services.client.CollectionSpaceClient;
import org.collectionspace.services.client.IQueryManager;
import org.collectionspace.services.client.PoxPayloadIn;
import org.collectionspace.services.client.PoxPayloadOut;
import org.collectionspace.services.client.workflow.WorkflowClient;
-import org.collectionspace.services.common.ResourceMap;
import org.collectionspace.services.common.ServiceMain;
import org.collectionspace.services.common.UriTemplateRegistry;
import org.collectionspace.services.common.api.RefName;
-import org.collectionspace.services.common.api.RefNameUtils;
import org.collectionspace.services.common.api.Tools;
-import org.collectionspace.services.common.api.RefNameUtils.AuthorityInfo;
import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
import org.collectionspace.services.common.context.MultipartServiceContext;
import org.collectionspace.services.common.context.ServiceContext;
import org.collectionspace.services.common.vocabulary.AuthorityServiceUtils;
import org.collectionspace.services.common.vocabulary.RefNameServiceUtils;
import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
-import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
import org.collectionspace.services.config.service.ListResultField;
import org.collectionspace.services.config.service.ObjectPartType;
import org.collectionspace.services.nuxeo.util.NuxeoUtils;
import org.collectionspace.services.relation.RelationsCommonList;
import org.collectionspace.services.vocabulary.VocabularyItemJAXBSchema;
+
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.model.PropertyException;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.UriInfo;
-
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
}
/**
- * We need to move the local item to the SAS workflow state. This might involve multiple transitions.
+ * We need to change the local item's state to one that maps to the replication server's workflow
+ * state. This might involve making multiple transitions.
*
* WIKI:
- * See table at https://wiki.collectionspace.org/pages/viewpage.action?pageId=162496564
+ * See table at https://wiki.collectionspace.org/pages/viewpage.action?pageId=162496564
*
*/
private List<String> getTransitionList(String sasWorkflowState, String localItemWorkflowState) throws DocumentException {
List<String> result = new ArrayList<String>();
//
- // The first set of conditions maps a SAS "project" state to a local state of "locked"
+ // The first set of conditions maps a replication-server "project" state to a local client state of "replicated"
//
if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
- result.add(WorkflowClient.WORKFLOWTRANSITION_LOCK);
+ result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
} else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
- result.add(WorkflowClient.WORKFLOWTRANSITION_LOCK);
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED)) {
+ result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
// Do nothing. We're good with this state
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED)) {
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
//
- // The second set of conditions maps a SAS "deleted" state to a local state of "deleted"
+ // The second set of conditions maps a replication-server "deleted" state to a local client state of "deleted"
//
} else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
- result.add(WorkflowClient.WORKFLOWTRANSITION_LOCK);
+ result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
} else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
- result.add(WorkflowClient.WORKFLOWTRANSITION_LOCK);
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED)) {
+ result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED)) {
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
// Do nothing. We're good with this state
//
- // The third set of conditions maps a SAS "locked" state to a local state of "locked"
+ // The third set of conditions maps a replication-server "replicated" state to a local state of "replicated"
//
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
- result.add(WorkflowClient.WORKFLOWTRANSITION_LOCK);
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
+ result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
- result.add(WorkflowClient.WORKFLOWTRANSITION_LOCK);
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED)) {
+ result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
// Do nothing. We're good with this state
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED)) {
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
//
- // The last set of conditions maps a SAS "locked_deleted" state to a local state of "deleted"
+ // The last set of conditions maps a replication-server "replicated_deleted" state to a local client state of "deleted"
//
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
- result.add(WorkflowClient.WORKFLOWTRANSITION_LOCK);
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
+ result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
- result.add(WorkflowClient.WORKFLOWTRANSITION_LOCK);
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED)) {
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
+ result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
- } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED)) {
+ } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
// Do nothing. We're good with this state
} else {
//
if (status == Response.Status.FORBIDDEN.getStatusCode()) {
result = false;
}
+ response.close();
return result;
}
import org.collectionspace.services.client.PoxPayloadOut;
import org.collectionspace.services.client.XmlTools;
import org.collectionspace.services.jaxb.AbstractCommonList;
-
import org.dom4j.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
protected static final String SAS_IDENTIFIER = "SAS";
protected String knownSASAuthorityResourceId = null;
+ protected String knownSASAuthorityResourceIdentifier = null;
protected String knownSASItemResourceId = null;
protected HashMap<String, String> allSASResourceItemIdsCreated = new HashMap<String, String>(); /* itemURN, parentURN */;
//
String localAuthorityId = null;
try {
- localAuthorityId = createResource(client, testName, getSASAuthorityIdentifier());
+ localAuthorityId = createResource(client, testName, knownSASAuthorityResourceIdentifier, false);
} catch (Exception e) {
Assert.assertNotNull(localAuthorityId);
}
// Now we can try to sync the SAS authority with the local one we just created.
//
setupSync();
- Response response = client.syncByName(getSASAuthorityIdentifier()); // Notice we're using the Short ID (short ID is the same on the local and SAS)
+ Response response = client.syncByName(knownSASAuthorityResourceIdentifier); // Notice we're using the Short ID (short ID is the same on the local and SAS)
try {
int statusCode = response.getStatus();
if (logger.isDebugEnabled()) {
response.close();
}
}
-
+
/**
* SAS - Create a new authority on the SAS server.
* @param testName
setupCreate();
try {
- String newID = createResource(getSASClientInstance(), testName, getSASAuthorityIdentifier());
+ String newID = createResource(getSASClientInstance(), testName, getSASAuthorityIdentifier(), true);
knownSASAuthorityResourceId = newID;
+ knownSASAuthorityResourceIdentifier = getShortId(getSASClientInstance(), knownSASAuthorityResourceId);
if (logger.isDebugEnabled()) {
String.format("Created SAS authority '%s' with CSID=%s.", getSASAuthorityIdentifier(), newID);
}
}
}
- private String getShortId(String authorityCsid) throws Exception {
+ private String getShortId(AuthorityClient client, String authorityCsid) throws Exception {
String result = null;
// Submit the request to the service and store the response.
- AuthorityClient client = (AuthorityClient) getClientInstance();
Response res = client.read(authorityCsid);
try {
int statusCode = res.getStatus();
return result;
}
+
+ private String getShortId(String authorityCsid) throws Exception {
+ AuthorityClient client = (AuthorityClient) getClientInstance();
+ return getShortId(client, authorityCsid);
+ }
/**
* Read by name.
String result = null;
CollectionSpaceClient client = this.getClientInstance();
- result = createResource(client, testName, identifier);
+ result = createResource(client, testName, identifier, false);
return result;
}
- protected String createResource(CollectionSpaceClient client, String testName, String identifier) throws Exception {
+ protected String createResource(CollectionSpaceClient client, String testName, String identifier, boolean makeUnique) throws Exception {
String result = null;
setupCreate();
+
+ if (makeUnique == true) {
+ identifier = identifier + Math.abs(random.nextInt());
+ }
+
REQUEST_TYPE payload = createInstance(client.getCommonPartName(), identifier);
Response res = client.create(payload);
try {
public static final String SERVICE_PAYLOAD_NAME = SERVICE_NAME;
public static final String SERVICE_COMMONPART_NAME = SERVICE_NAME + PART_LABEL_SEPARATOR + PART_COMMON_LABEL;
public static final String SERVICE_AUTHZ_SUFFIX = "/*/" + SERVICE_PATH_COMPONENT + "/";
- //
- // Workflow states
- //
- public static final String WORKFLOWSTATE_XML_ELEMENT_NAME = COLLECTIONSPACE_CORE_WORKFLOWSTATE;
- public static final String WORKFLOWTRANSITION_UNDELETE = "undelete";
- public static final String WORKFLOWTRANSITION_DELETE = "delete";
- public static final String WORKFLOWSTATE_DELETED = "deleted";
- public static final String WORKFLOWSTATE_ACTIVE = "active"; // Is this a used state?
- public static final String WORKFLOWSTATE_PROJECT = "project";
+ /*
+ * Nuxeu document workflow states
+ */
- public static final String WORKFLOWTRANSITION_LOCK = "lock";
+ public static final String WORKFLOWSTATE_XML_ELEMENT_NAME = COLLECTIONSPACE_CORE_WORKFLOWSTATE;
+ // common to all Nuxeo document lifecycle policies
+ public static final String WORKFLOWSTATE_PROJECT = "project";
+ public static final String WORKFLOWSTATE_DELETED = "deleted";
+ // part of the "cs_replicating" Nuxeo document lifecycle policy
+ public static final String WORKFLOWSTATE_REPLICATED = "replicated";
+ public static final String WORKFLOWSTATE_REPLICATED_DELETED = "replicated_deleted";
+ // part of the "cs_locking" Nuxeo document lifecycle policy
public static final String WORKFLOWSTATE_LOCKED = "locked";
public static final String WORKFLOWSTATE_LOCKED_DELETED = "locked_deleted";
+
+ /*
+ * Nuxeo document workflow transition verbs
+ */
+ public static final String WORKFLOWTRANSITION_DELETE = "delete";
+ public static final String WORKFLOWTRANSITION_UNDELETE = "undelete";
+ public static final String WORKFLOWTRANSITION_LOCK = "lock";
+ public static final String WORKFLOWTRANSITION_UNLOCK = "unlock";
+ public static final String WORKFLOWTRANSITION_REPLICATE = "replicate";
+ public static final String WORKFLOWTRANSITION_UNREPLICATE = "unreplicate";
public static final String WORKFLOWTRANSITION_TO = "to";
//
- // DocumentHandler passed properties
+ // DocumentHandler workflow-related passed in context properties
//
public static final String TRANSITION_ID = "transition_id";
public static final String TRANSITION_PARAM_JAXRS = "transition";
+
//
// Service Query Params
//
}
}
+ /**
+ * Helps to filter for queries that either want to include or exclude documents in deleted workflow states.
+ *
+ * @param queryParams
+ * @return
+ */
private static String buildWorkflowWhereClause(MultivaluedMap<String, String> queryParams) {
String result = null;
String includeOnlyDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED);
if (includeDeleted != null && includeDeleted.equalsIgnoreCase(Boolean.FALSE.toString())) {
- result = String.format("(ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s')",
- WorkflowClient.WORKFLOWSTATE_DELETED, WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED);
+ result = String.format("(ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s')",
+ WorkflowClient.WORKFLOWSTATE_DELETED, WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED, WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED);
} else if (includeOnlyDeleted != null && includeOnlyDeleted.equalsIgnoreCase(Boolean.TRUE.toString())) {
- result = String.format("(ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s')",
- WorkflowClient.WORKFLOWSTATE_PROJECT, WorkflowClient.WORKFLOWSTATE_LOCKED);
+ result = String.format("(ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s')",
+ WorkflowClient.WORKFLOWSTATE_PROJECT, WorkflowClient.WORKFLOWSTATE_LOCKED, WorkflowClient.WORKFLOWSTATE_REPLICATED);
}
return result;
if (isAnonymousRequest(request, resourceMethodInvoker) == true) {
// We don't need to check credentials for anonymous requests. Just login to Nuxeo and
// exit
- nuxeoPreProcess(request, resourceMethodInvoker);
+ nuxeoPreProcess(request, resourceMethodInvoker); // We login to Nuxeo only after we've checked authorization
return result;
}
//
// Login to Nuxeo
//
- nuxeoPreProcess(request, resourceMethodInvoker);
+ nuxeoPreProcess(request, resourceMethodInvoker); // We login to Nuxeo only after we've checked authorization
//
// We've passed all the checks. Now just log the results
String.format("Framework logins: ", frameworkLogins);
}
} else {
- logger.warn(ERROR_NUXEO_LOGOUT);
+ if (frameworkLogins > 0) {
+ logger.warn(ERROR_NUXEO_LOGOUT); // If we get here, it means our login/logout bookkeeping has failed.
+ }
}
if (frameworkLogins == 0) {
}
/*
- * Maps the transition name to handle existing states like "locked" and "deleted". This allows us to do things like lock "deleted"
- * records, delete "locked" records, etc. For example, this code maps the transition name "delete" to "delete_locked" on records in the "locked" state.
- * As another example, it would map "undelete" to "undelete_locked" for locked records and just "undelete" for records in any other state.
+ * Maps the transition name to handle existing states like "replicated" and "deleted". This allows us to do things like replicate "deleted"
+ * records, delete "replicated" records, etc. For example, this code maps the transition name "delete" to "delete_replicated" on records in the "replicated" state.
+ * As another example, it would map "undelete" to "undelete_replicated" for replicated records and just "undelete" for records in any other state.
*
- * Essentially, this mapping allows REST API clients to use the "delete", "undelete", "lock", "unlock", etc transitions on records no matter what
+ * Essentially, this mapping allows REST API clients to use the "delete", "undelete", "replicate", "unreplicate", etc transitions on records no matter what
* their current state. Without this mapping, REST API clients would need to calculate this on their own and use the longer forms like:
- * "delete_locked", "undelete_locked", "lock_deleted", "unlocked_deleted", etc.
+ * "delete_replicated", "undelete_replicated", "lock_deleted", "unlock_deleted", etc.
*/
String getQualifiedTransitionName(DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) {
String result = null;
//Empty constructor
}
- public void assertWorkflowState(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
- DocumentModel docModel) throws DocumentNotFoundException, ClientException {
+ public void assertWorkflowState(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, DocumentModel docModel) throws DocumentNotFoundException, ClientException {
MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
if (queryParams != null) {
//
//
String currentState = docModel.getCurrentLifeCycleState();
String includeDeletedStr = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_NONDELETED);
- boolean includeDeleted = includeDeletedStr == null ? true : Boolean.parseBoolean(includeDeletedStr);
+ boolean includeDeleted = (includeDeletedStr == null) ? true : Boolean.parseBoolean(includeDeletedStr);
if (includeDeleted == false) {
//
// We don't wanted soft-deleted objects, so throw an exception if this one is soft-deleted.
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="type" type="xs:string" use="optional"/>
<xs:attribute name="version" type="types:VersionType" use="required"/>
- <xs:attribute name="supportsSynchronization" type="xs:boolean" default="false"/>
+ <xs:attribute name="supportsReplicating" type="xs:boolean" default="false"/>
</xs:complexType>
<!--
* @param headerLabel the header label
* @return the multipart output
*/
- public static PoxPayloadOut createPersonAuthorityInstance(
- String displayName, String shortIdentifier, String headerLabel ) {
+ public static PoxPayloadOut createPersonAuthorityInstance(String displayName, String shortIdentifier, String headerLabel ) {
PersonauthoritiesCommon personAuthority = new PersonauthoritiesCommon();
personAuthority.setDisplayName(displayName);
- personAuthority.setShortIdentifier(shortIdentifier + random.nextInt(10000));
+ personAuthority.setShortIdentifier(shortIdentifier);
//String refName = createPersonAuthRefName(shortIdentifier, displayName);
//personAuthority.setRefName(refName);
personAuthority.setVocabType("PersonAuthority");
PayloadOutputPart commonPart = multipart.addPart(personAuthority, MediaType.APPLICATION_XML_TYPE);
commonPart.setLabel(headerLabel);
- if(logger.isDebugEnabled()){
- logger.debug("to be created, personAuthority common ",
+ if (logger.isDebugEnabled()) {
+ logger.debug("To be created, personAuthority common: ",
personAuthority, PersonauthoritiesCommon.class);
}
public static PoxPayloadOut createPersonInstance(String inAuthority,
String personAuthRefName,
Map<String, String> personInfo,
- List<PersonTermGroup> terms,
+ List<PersonTermGroup> terms,
String headerLabel){
if (terms == null || terms.isEmpty()) {
terms = getTermGroupInstance(getGeneratedIdentifier());
* @return the multipart output
*/
public static PoxPayloadOut createPersonInstance(String inAuthority,
- String personAuthRefName, Map<String, String> personInfo,
- List<PersonTermGroup> terms,
- Map<String, List<String>> personRepeatablesInfo, String headerLabel){
+ String personAuthRefName,
+ Map<String, String> personInfo,
+ List<PersonTermGroup> terms,
+ Map<String, List<String>> personRepeatablesInfo,
+ String headerLabel){
PersonsCommon person = new PersonsCommon();
person.setInAuthority(inAuthority);
String shortId = personInfo.get(PersonJAXBSchema.SHORT_IDENTIFIER);
import org.collectionspace.services.person.PersonTermGroup;
import org.collectionspace.services.person.PersonTermGroupList;
import org.collectionspace.services.person.PersonsCommon;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
@Override
protected PoxPayloadOut createInstance(String identifier) {
PersonAuthorityClient client = new PersonAuthorityClient();
+
String displayName = "displayName-" + identifier;
PoxPayloadOut multipart = PersonAuthorityClientUtils.createPersonAuthorityInstance(
displayName, identifier, client.getCommonPartName());
+
return multipart;
}
return newID;
}
+ /*
+ * This override asks for a unique identifier (short ID in the case of authority tests).
+ *
+ * (non-Javadoc)
+ * @see org.collectionspace.services.client.test.AbstractServiceTestImpl#createResource(java.lang.String, java.lang.String)
+ */
+ @Override
+ protected String createResource(String testName, String identifier) throws Exception {
+ String result = null;
+
+ CollectionSpaceClient client = this.getClientInstance();
+ result = createResource(client, testName, identifier, true);
+
+ return result;
+ }
+
/**
* Creates the contact.
*
private static final String ERROR_TERMS_IN_WORKFLOWSTATE = "Cannot modify a relationship if either end is in the workflow state: ";
-
+ /*
+ * Will return 'true' if either the subject's or object's current workflow state *contain* the passed in workflow
+ * state.
+ *
+ * For example:
+ * - will return 'true' if the subject's workflow state is "replicated_deleted" and the passed in workflow state is "replicated" or "deleted".
+ * - will return 'true' if the subject's or object's workflow state is "locked" and the passed in workflow state is "locked"
+ */
private boolean subjectOrObjectInWorkflowState(DocumentWrapper<DocumentModel> wrapDoc, String workflowState) throws ServiceException {
boolean result = false;
DocumentModel relationDocModel = wrapDoc.getWrappedObject();
try {
DocumentModel subjectDocModel = getSubjectOrObjectDocModel(repoSession, relationDocModel, SUBJ_DOC_MODEL);
DocumentModel objectDocModel = getSubjectOrObjectDocModel(repoSession, relationDocModel, OBJ_DOC_MODEL);
- if (subjectDocModel.getCurrentLifeCycleState().equalsIgnoreCase(workflowState) ||
- objectDocModel.getCurrentLifeCycleState().equalsIgnoreCase(workflowState)) {
+ if (subjectDocModel.getCurrentLifeCycleState().contains(workflowState) ||
+ objectDocModel.getCurrentLifeCycleState().contains(workflowState)) {
result = true;
}
} catch (Exception e) {
@Override
/*
- * Until we rework the RepositoryClient to handle the workflow transition (just like it does for 'create', 'get', 'update', and 'delete', this method will only check to see if the transition is allowed. Until then,
- * the WorkflowDocumentModelHandler class does the actual workflow transition.
+ * Until we rework the RepositoryClient to handle the workflow transition (just like it does for 'create', 'get', 'update', and 'delete'), this method will only check to see
+ * if the transition is allowed. Until then, the WorkflowDocumentModelHandler class does the actual workflow transition.
*
* @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#handleWorkflowTransition(org.collectionspace.services.common.document.DocumentWrapper, org.collectionspace.services.lifecycle.TransitionDef)
*/
// And take care of ensuring all the values for the relation info are correct
populateSubjectAndObjectValues(wrapDoc);
- // Neither the subject nor the object can be locked
+ // We can't create a relationship record if either the subject or the object is in a locked workflow state
String workflowState = WorkflowClient.WORKFLOWSTATE_LOCKED;
if (subjectOrObjectInWorkflowState(wrapDoc, workflowState) == true) {
throw new ServiceException(HttpURLConnection.HTTP_FORBIDDEN,
repoSession = repoClient.getRepositorySession(ctx);
releaseRepoSession = true;
}
+
DocumentFilter myFilter = getDocumentFilter();
- // Make sure we pick up workflow state, etc.
int pageSize = myFilter.getPageSize();
int pageNum = myFilter.getStartPage();
list.setPageNum(pageNum);
list.setPageSize(pageSize);
-
try {
Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
RepositoryClientImpl nuxeoRepoClient = (RepositoryClientImpl)repoClient;
// Get the service bindings for this tenant
- TenantBindingConfigReaderImpl tReader =
- ServiceMain.getInstance().getTenantBindingConfigReader();
+ TenantBindingConfigReaderImpl tReader = ServiceMain.getInstance().getTenantBindingConfigReader();
// We need to get all the procedures, authorities, and objects.
List<ServiceBindingType> servicebindings =
tReader.getServiceBindingsByType(ctx.getTenantId(), serviceGroupNames);
ServiceMessages.resourceNotFoundMsg(implode(serviceGroupNames, ","))).type("text/plain").build();
throw new CSWebApplicationException(response);
}
+
servicebindings = SecurityUtils.getReadableServiceBindingsForCurrentUser(servicebindings);
// Build the list of docTypes for allowed serviceBindings
ArrayList<String> docTypes = new ArrayList<String>();
}
}
- // This should be "Document" but CMIS is gagging on that right now.
+ // This should be type "Document" but CMIS is gagging on that right now.
ctx.getQueryParams().add(IQueryManager.SELECT_DOC_TYPE_FIELD, QueryManagerNuxeoImpl.COLLECTIONSPACE_DOCUMENT_TYPE);
// Now we have to issue the search
- // findDocs will build a QueryContext, which wants to see a docType for our context
+ // The findDocs() method will build a QueryContext, which wants to see a docType for our context
ctx.setDocumentType(QueryManagerNuxeoImpl.NUXEO_DOCUMENT_TYPE);
DocumentWrapper<DocumentModelList> docListWrapper =
nuxeoRepoClient.findDocs(ctx, this, repoSession, docTypes );
// Now we gather the info for each document into the list and return
DocumentModelList docList = docListWrapper.getWrappedObject();
-
if (docList == null) { // found no authRef fields - nothing to process
return list;
}
// Move this to a Utils class!
public static String implode(List<String> stringList, String sep) {
StringBuilder sb = new StringBuilder();
+
boolean fFirst = false;
- for(String name:stringList) {
+ for (String name:stringList) {
if(fFirst) {
fFirst = false;
} else {
}
sb.append(name);
}
+
return sb.toString();
}
return "/" + sb.getName().toLowerCase() + "/" + csid;
}
- private void processDocList(
- String tenantId,
- DocumentModelList docList,
- Map<String, ServiceBindingType> queriedServiceBindings,
- CommonList list ) {
- int nFields = NUM_META_FIELDS+NUM_STANDARD_LIST_RESULT_FIELDS;
- String fields[] = new String[nFields];
+ private void processDocList(String tenantId,
+ DocumentModelList docList,
+ Map<String, ServiceBindingType> queriedServiceBindings,
+ CommonList list) {
+ String fields[] = new String[NUM_META_FIELDS + NUM_STANDARD_LIST_RESULT_FIELDS];
fields[0] = STANDARD_LIST_CSID_FIELD;
fields[1] = STANDARD_LIST_URI_FIELD;
fields[2] = STANDARD_LIST_UPDATED_AT_FIELD;
fields[6] = DOC_NUMBER_FIELD;
fields[7] = DOC_TYPE_FIELD;
list.setFieldsReturned(fields);
+
Iterator<DocumentModel> iter = docList.iterator();
HashMap<String, Object> item = new HashMap<String, Object>();
while (iter.hasNext()) {
throw new RuntimeException(
"processDocList: No Service Binding for docType: " + docType);
}
+
String csid = NuxeoUtils.getCsid(docModel);
item.put(STANDARD_LIST_CSID_FIELD, csid);
UriTemplateRegistry uriTemplateRegistry = ServiceMain.getInstance().getUriTemplateRegistry();
StoredValuesUriTemplate storedValuesResourceTemplate = uriTemplateRegistry.get(new UriTemplateRegistryKey(tenantId, docType));
- Map<String, String> additionalValues = new HashMap<String, String>();
- if (storedValuesResourceTemplate.getUriTemplateType() == UriTemplateFactory.ITEM) {
+ Map<String, String> additionalValues = new HashMap<String, String>();
+ if (storedValuesResourceTemplate.getUriTemplateType() == UriTemplateFactory.ITEM) {
try {
String inAuthorityCsid = (String) NuxeoUtils.getProperyValue(docModel, "inAuthority"); //docModel.getPropertyValue("inAuthority"); // AuthorityItemJAXBSchema.IN_AUTHORITY
additionalValues.put(UriTemplateFactory.IDENTIFIER_VAR, inAuthorityCsid);
String msg = String.format("Could not extract inAuthority property from authority item with CSID = ", docModel.getName());
logger.warn(msg, e);
}
- } else {
+ } else {
additionalValues.put(UriTemplateFactory.IDENTIFIER_VAR, csid);
}
+
String uriStr = storedValuesResourceTemplate.buildUri(additionalValues);
item.put(STANDARD_LIST_URI_FIELD, uriStr);
-
try {
item.put(STANDARD_LIST_UPDATED_AT_FIELD, getUpdatedAtAsString(docModel));
item.put(STANDARD_LIST_WORKFLOW_FIELD, docModel.getCurrentLifeCycleState());
logger.error("Error getting core values for doc ["+csid+"]: "+e.getLocalizedMessage());
}
- String value = ServiceBindingUtils.getMappedFieldInDoc(sb,
- ServiceBindingUtils.OBJ_NUMBER_PROP, docModel);
+ String value = ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NUMBER_PROP, docModel);
if (value != null) {
item.put(DOC_NUMBER_FIELD, value);
}
- value = ServiceBindingUtils.getMappedFieldInDoc(sb,
- ServiceBindingUtils.OBJ_NAME_PROP, docModel);
+ value = ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NAME_PROP, docModel);
if (value != null) {
item.put(DOC_NAME_FIELD, value);
}
list.addItem(item);
item.clear();
}
-
}
-
-
}