import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
+import org.collectionspace.services.client.AbstractCommonListUtils;
import org.collectionspace.services.client.workflow.WorkflowClient;
import org.collectionspace.services.common.api.Tools;
+import org.collectionspace.services.common.api.Tools.NoRelatedRecordsException;
import org.collectionspace.services.movement.nuxeo.MovementConstants;
import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
import org.collectionspace.services.nuxeo.client.java.CoreSessionWrapper;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.event.DocumentEventTypes;
+import org.nuxeo.ecm.core.api.impl.DocumentModelImpl;
+import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
import org.nuxeo.ecm.core.event.Event;
import org.nuxeo.ecm.core.event.EventContext;
import org.nuxeo.ecm.core.event.EventListener;
protected final static String COLLECTIONOBJECTS_COMMON_SCHEMA = "collectionobjects_common"; // FIXME: Get from external constant
private final static String COLLECTIONOBJECT_DOCTYPE = "CollectionObject"; // FIXME: Get from external constant
protected final static String COMPUTED_CURRENT_LOCATION_PROPERTY = "computedCurrentLocation"; // FIXME: Create and then get from external constant
+ private final static String CURRENT_LOCATION_ELEMENT_NAME = "currentLocation"; // From movement_commons schema. FIXME: Get from external constant that already exists
protected final static String MOVEMENTS_COMMON_SCHEMA = "movements_common"; // FIXME: Get from external constant
private final static String MOVEMENT_DOCTYPE = MovementConstants.NUXEO_DOCTYPE;
private final static String LOCATION_DATE_PROPERTY = "locationDate"; // FIXME: Get from external constant
protected final static String UPDATED_AT_PROPERTY = "updatedAt"; // FIXME: Get from external constant
private final static String NONVERSIONED_NONPROXY_DOCUMENT_WHERE_CLAUSE_FRAGMENT =
"AND ecm:isCheckedInVersion = 0"
- + "AND ecm:isProxy = 0 ";
+ + " AND ecm:isProxy = 0 ";
private final static String ACTIVE_DOCUMENT_WHERE_CLAUSE_FRAGMENT =
"AND (ecm:currentLifeCycleState <> 'deleted') "
+ NONVERSIONED_NONPROXY_DOCUMENT_WHERE_CLAUSE_FRAGMENT;
continue;
}
// Get the CollectionObject's most recent, related Movement.
- mostRecentMovementDocModel = getMostRecentMovement(coreSession, collectionObjectCsid,
- isAboutToBeRemovedEvent, movementCsidToFilter);
- if (mostRecentMovementDocModel == null) {
- continue;
- }
+
+ try {
+ mostRecentMovementDocModel = getMostRecentMovement(coreSession, collectionObjectCsid,
+ isAboutToBeRemovedEvent, movementCsidToFilter);
+ if (mostRecentMovementDocModel == null) {
+ // This means we couldn't figure out which Movement record to use.
+ continue;
+ }
+ } catch (NoRelatedRecordsException e) {
+ // This means there were NO active Movement records to use.
+ mostRecentMovementDocModel = new DocumentModelImpl(MOVEMENT_DOCTYPE); // Create an empty document model
+ }
+
// Update the CollectionObject with values from that Movement.
collectionObjectDocModel =
updateCollectionObjectValuesFromMovement(collectionObjectDocModel, mostRecentMovementDocModel);
COMPUTED_CURRENT_LOCATION_PROPERTY);
logger.trace("computedCurrentLocation refName after value update=" + computedCurrentLocationRefName);
}
+
coreSession.saveDocument(collectionObjectDocModel);
}
}
* CSID.
*/
protected static DocumentModel getCurrentDocModelFromCsid(CoreSessionInterface session, String collectionObjectCsid) {
- DocumentModelList collectionObjectDocModels = null;
+ DocumentModelList docModelList = null;
try {
final String query = "SELECT * FROM "
+ NuxeoUtils.BASE_DOCUMENT_TYPE
+ NuxeoUtils.getByNameWhereClause(collectionObjectCsid)
+ " "
+ NONVERSIONED_NONPROXY_DOCUMENT_WHERE_CLAUSE_FRAGMENT;
- collectionObjectDocModels = session.query(query);
+ docModelList = session.query(query);
} catch (Exception e) {
logger.warn("Exception in query to get active document model for CollectionObject: ", e);
}
- if (collectionObjectDocModels == null || collectionObjectDocModels.isEmpty()) {
+ if (docModelList == null || docModelList.isEmpty()) {
logger.warn("Could not get active document models for CollectionObject(s).");
return null;
- } else if (collectionObjectDocModels.size() != 1) {
+ } else if (docModelList.size() != 1) {
logger.debug("Found more than 1 active document with CSID=" + collectionObjectCsid);
return null;
}
- return collectionObjectDocModels.get(0);
+ return docModelList.get(0);
}
// FIXME: A quick first pass, using an only partly query-based technique for
*/
protected static DocumentModel getMostRecentMovement(CoreSessionInterface session, String collectionObjectCsid,
boolean isAboutToBeRemovedEvent, String aboutToBeRemovedMovementCsidToFilter)
- throws ClientException {
+ throws ClientException, Tools.NoRelatedRecordsException {
DocumentModel mostRecentMovementDocModel = null;
// Get Relation records for Movements related to this CollectionObject.
//
logger.trace("Found " + relationDocModels.size() + " relations to Movement records to/from this CollectionObject record.");
}
}
+
+ //
+ // Remove redundant document models from the list.
+ //
+ relationDocModels = removeRedundantRelations(relationDocModels);
+
+ //
+ // Remove relationships with inactive movement records
+ //
+ relationDocModels = removeRelationsWithInactiveMovements(session, relationDocModels);
+
+ if (relationDocModels == null || relationDocModels.size() == 0) throw new Tools.NoRelatedRecordsException();
+
+ //
+ // If there is only one related movement record, then return it as the most recent
+ // movement record -if it's current location element is not empty.
+ //
+ if (relationDocModels.size() == 1) {
+ DocumentModel relationDocModel = relationDocModels.get(0);
+ DocumentModel movementDocModel = getMovementDocModelFromRelation(session, relationDocModel);
+ String currentLocation = (String) movementDocModel.getProperty(MOVEMENTS_COMMON_SCHEMA, CURRENT_LOCATION_ELEMENT_NAME);
+ if (Tools.isBlank(currentLocation) || !isActiveDocument(movementDocModel)) { // currentLocation must be set and record must be active
+ movementDocModel = null;
+ }
+
+ return movementDocModel;
+ }
+
// Iterate through related movement records, to find the related
// Movement record with the most recent location date.
GregorianCalendar mostRecentLocationDate = EARLIEST_COMPARISON_DATE;
}
}
}
+
return mostRecentMovementDocModel;
}
+
+ //
+ // This method assumes that the relation passed into this method is between a Movement record
+ // and a CollectionObject (cataloging) record.
+ //
+ private static DocumentModel getMovementDocModelFromRelation(CoreSessionInterface session, DocumentModel relationDocModel) {
+ String movementCsid = null;
+
+ String subjectDocType = (String) relationDocModel.getProperty(RELATIONS_COMMON_SCHEMA, SUBJECT_DOCTYPE_PROPERTY);
+ if (subjectDocType.endsWith(MOVEMENT_DOCTYPE)) {
+ movementCsid = (String) relationDocModel.getProperty(RELATIONS_COMMON_SCHEMA, SUBJECT_CSID_PROPERTY);
+ } else {
+ movementCsid = (String) relationDocModel.getProperty(RELATIONS_COMMON_SCHEMA, OBJECT_CSID_PROPERTY);
+ }
- /**
+ return getCurrentDocModelFromCsid(session, movementCsid);
+ }
+
+ //
+ // Compares two Relation document models to see if they're either identical or
+ // reciprocal equivalents.
+ //
+ private static boolean compareRelationDocModels(DocumentModel r1, DocumentModel r2) {
+ boolean result = false;
+
+ String r1_subjectDocType = (String) r1.getProperty(RELATIONS_COMMON_SCHEMA, SUBJECT_DOCTYPE_PROPERTY);
+ String r1_objectDocType = (String) r1.getProperty(RELATIONS_COMMON_SCHEMA, OBJECT_DOCTYPE_PROPERTY);
+ String r1_subjectCsid = (String) r1.getProperty(RELATIONS_COMMON_SCHEMA, SUBJECT_CSID_PROPERTY);
+ String r1_objectCsid = (String) r1.getProperty(RELATIONS_COMMON_SCHEMA, OBJECT_CSID_PROPERTY);
+
+ String r2_subjectDocType = (String) r2.getProperty(RELATIONS_COMMON_SCHEMA, SUBJECT_DOCTYPE_PROPERTY);
+ String r2_objectDocType = (String) r2.getProperty(RELATIONS_COMMON_SCHEMA, OBJECT_DOCTYPE_PROPERTY);
+ String r2_subjectCsid = (String) r2.getProperty(RELATIONS_COMMON_SCHEMA, SUBJECT_CSID_PROPERTY);
+ String r2_objectCsid = (String) r2.getProperty(RELATIONS_COMMON_SCHEMA, OBJECT_CSID_PROPERTY);
+
+ // Check to see if they're identical
+ if (r1_subjectDocType.equalsIgnoreCase(r2_subjectDocType) && r1_objectDocType.equalsIgnoreCase(r2_objectDocType)
+ && r1_subjectCsid.equalsIgnoreCase(r2_subjectCsid) && r1_objectCsid.equalsIgnoreCase(r2_objectCsid)) {
+ return true;
+ }
+
+ // Check to see if they're reciprocal
+ if (r1_subjectDocType.equalsIgnoreCase(r2_objectDocType) && r1_objectDocType.equalsIgnoreCase(r2_subjectDocType)
+ && r1_subjectCsid.equalsIgnoreCase(r2_objectCsid) && r1_objectCsid.equalsIgnoreCase(r2_subjectCsid)) {
+ return true;
+ }
+
+ return result;
+ }
+
+ //
+ // Return a Relation document model list with redundant (either identical or reciprocal) relations removed.
+ //
+ private static DocumentModelList removeRedundantRelations(DocumentModelList relationDocModelList) {
+ DocumentModelList resultList = new DocumentModelListImpl();
+ for (DocumentModel relationDocModel : relationDocModelList) {
+ if (existsInResultList(resultList, relationDocModel) == false) {
+ resultList.add(relationDocModel);
+ }
+ }
+ // TODO Auto-generated method stub
+ return resultList;
+ }
+
+ //
+ // Return just the list of relationships with active Movement records
+ //
+ private static DocumentModelList removeRelationsWithInactiveMovements(CoreSessionInterface session, DocumentModelList relationDocModelList) {
+ DocumentModelList resultList = new DocumentModelListImpl();
+
+ for (DocumentModel relationDocModel : relationDocModelList) {
+ String movementCsid = getCsidForDesiredDocTypeFromRelation(relationDocModel, MOVEMENT_DOCTYPE, COLLECTIONOBJECT_DOCTYPE);
+ DocumentModel movementDocModel = getCurrentDocModelFromCsid(session, movementCsid);
+ if (isActiveDocument(movementDocModel) == true) {
+ resultList.add(relationDocModel);
+ } else {
+ logger.trace(String.format("Disqualified relationship with inactive Movement record=%s.", movementCsid));
+ }
+ }
+
+ return resultList;
+ }
+
+
+ //
+ // Check to see if the Relation (or its equivalent reciprocal) is already in the list.
+ //
+ private static boolean existsInResultList(DocumentModelList relationDocModelList, DocumentModel relationDocModel) {
+ boolean result = false;
+
+ for (DocumentModel target : relationDocModelList) {
+ if (compareRelationDocModels(relationDocModel, target) == true) {
+ result = true;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ /**
* Returns the CSID for a desired document type from a Relation record,
* where the relationship involves two specified, different document types.
*
protected DocumentModel updateComputedCurrentLocationValue(DocumentModel collectionObjectDocModel,
DocumentModel movementDocModel)
throws ClientException {
+
+ //
+ // First see if we're being asked to clear the computed location field
+ //
+ if (movementDocModel.getCoreSession() == null) {
+ collectionObjectDocModel.setProperty(COLLECTIONOBJECTS_COMMON_SCHEMA,
+ COMPUTED_CURRENT_LOCATION_PROPERTY, null);
+ return collectionObjectDocModel;
+ }
// Get the current location value from the Movement (the "new" value)
String currentLocationRefName =
logger.trace("computedCurrentLocation refName does NOT require updating.");
}
}
+
return collectionObjectDocModel;
}
}
\ No newline at end of file
</xs:complexType>
</xs:element>
<xs:element name="supportsNoContext" type="xs:boolean" />
- <xs:element name="forSingleDoc" type="xs:boolean" />
+ <xs:element name="supportsSingleDoc" type="xs:boolean" />
+ <xs:element name="supportsDocList" type="xs:boolean" />
+ <xs:element name="supportsGroup" type="xs:boolean" />
+ <!-- Batch specific fields -->
<xs:element name="createsNewFocus" type="xs:boolean" />
<xs:element name="className" type="xs:string" />
</xs:sequence>
import java.util.List;
import javax.ws.rs.core.Response;
import org.collectionspace.services.common.ResourceMap;
+import org.collectionspace.services.common.context.ServiceContext;
import org.collectionspace.services.common.invocable.InvocationContext;
import org.collectionspace.services.common.invocable.InvocationResults;
import org.slf4j.Logger;
"Could not find required CSID values in the invocation context for this batch job.";
private List<String> invocationModes;
private ResourceMap resourceMap;
- private InvocationContext context;
+ private InvocationContext invocationCtx;
+ private ServiceContext ctx;
+
private int completionStatus;
private InvocationResults results;
private InvocationError errorInfo;
private void init() {
this.invocationModes = Collections.emptyList();
this.resourceMap = null;
- this.context = null;
+ this.invocationCtx = null;
this.completionStatus = STATUS_UNSTARTED;
this.results = new InvocationResults();
this.errorInfo = null;
this.resourceMap = resourceMap;
}
+ @Override
+ public void setServiceContext(ServiceContext context) {
+ this.ctx = context;
+ }
+
+ @Override
+ public ServiceContext getServiceContext() {
+ return ctx;
+ }
+
+ @Override
public InvocationContext getInvocationContext() {
- return context;
+ return invocationCtx;
}
@Override
public void setInvocationContext(InvocationContext context) {
- this.context = context;
+ this.invocationCtx = context;
}
@Override
package org.collectionspace.services.batch;
-import java.util.HashMap;
-import java.util.Set;
-
-import org.collectionspace.services.common.NuxeoBasedResource;
import org.collectionspace.services.common.ResourceMap;
import org.collectionspace.services.common.invocable.Invocable;
@POST
@Path("{csid}")
public InvocationResults invokeBatchJob(
- @Context ResourceMap resourceMap,
+ @Context ResourceMap resourceMap,
+ @Context UriInfo ui,
@PathParam("csid") String csid,
InvocationContext invContext) {
try {
- ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
+ ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
BatchDocumentModelHandler handler = (BatchDocumentModelHandler)createDocumentHandler(ctx);
return handler.invokeBatchJob(ctx, csid, resourceMap, invContext);
protected final int BAD_REQUEST_STATUS = Response.Status.BAD_REQUEST.getStatusCode();
public InvocationResults invokeBatchJob(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String csid,
- ResourceMap resourceMap, InvocationContext invContext) throws Exception {
+ ResourceMap resourceMap, InvocationContext invocationCtx) throws Exception {
CoreSessionInterface repoSession = null;
boolean releaseRepoSession = false;
- String invocationMode = invContext.getMode();
+ String invocationMode = invocationCtx.getMode();
String modeProperty = null;
boolean checkDocType = true;
if (BatchInvocable.INVOCATION_MODE_SINGLE.equalsIgnoreCase(invocationMode)) {
}
if (checkDocType) {
List<String> forDocTypeList = (List<String>) NuxeoUtils.getProperyValue(docModel, BatchJAXBSchema.FOR_DOC_TYPES); //docModel.getPropertyValue(BatchJAXBSchema.FOR_DOC_TYPES);
- if (forDocTypeList == null || !forDocTypeList.contains(invContext.getDocType())) {
+ if (forDocTypeList == null || !forDocTypeList.contains(invocationCtx.getDocType())) {
throw new BadRequestException("BatchResource: Invoked with unsupported document type: "
- + invContext.getDocType());
+ + invocationCtx.getDocType());
}
}
className = (String) NuxeoUtils.getProperyValue(docModel, BatchJAXBSchema.BATCH_CLASS_NAME); //docModel.getPropertyValue(BatchJAXBSchema.BATCH_CLASS_NAME);
throw new BadRequestException("BatchResource: Invoked with unsupported context mode: " + invocationMode);
}
- batchInstance.setInvocationContext(invContext);
+ batchInstance.setInvocationContext(invocationCtx);
+ batchInstance.setServiceContext(ctx);
if (resourceMap != null) {
batchInstance.setResourceMap(resourceMap);
package org.collectionspace.services.batch.nuxeo;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import javax.ws.rs.core.Response;
import org.collectionspace.services.common.NuxeoBasedResource;
import org.collectionspace.services.common.ResourceMap;
import org.collectionspace.services.common.api.GregorianCalendarDateTimeUtils;
+import org.collectionspace.services.common.context.ServiceContext;
import org.collectionspace.services.common.invocable.InvocationContext;
import org.collectionspace.services.common.invocable.InvocationResults;
import org.collectionspace.services.client.LoanoutClient;
public class CreateAndLinkLoanOutBatchJob implements BatchInvocable {
private static ArrayList<String> invocationModes = null;
- private InvocationContext context;
+ private InvocationContext invocationCtx;
+ private ServiceContext ctx;
private int completionStatus;
private ResourceMap resourceMap;
private InvocationResults results;
public CreateAndLinkLoanOutBatchJob() {
CreateAndLinkLoanOutBatchJob.setupClassStatics();
- context = null;
+ invocationCtx = null;
completionStatus = STATUS_UNSTARTED;
resourceMap = null;
results = new InvocationResults();
return CreateAndLinkLoanOutBatchJob.invocationModes;
}
+ @Override
+ public void setServiceContext(ServiceContext context) {
+ this.ctx = context;
+ }
+
+ @Override
+ public ServiceContext getServiceContext() {
+ return ctx;
+ }
+
+ @Override
+ public InvocationContext getInvocationContext() {
+ return invocationCtx;
+ }
+
/**
* Sets the invocation context for the batch job. Called before run().
* @param context an instance of InvocationContext.
*/
+ @Override
public void setInvocationContext(InvocationContext context) {
- this.context = context;
+ this.invocationCtx = context;
}
/**
* Sets the invocation context for the batch job. Called before run().
- * @param context an instance of InvocationContext.
+ * @param invocationCtx an instance of InvocationContext.
*/
public void setResourceMap(ResourceMap resourceMap) {
this.resourceMap = resourceMap;
try {
// First, create the Loanout
if(createLoan() != STATUS_ERROR) {
- if(INVOCATION_MODE_SINGLE.equalsIgnoreCase(context.getMode())) {
+ if(INVOCATION_MODE_SINGLE.equalsIgnoreCase(invocationCtx.getMode())) {
if(createRelation(results.getPrimaryURICreated(),
- context.getSingleCSID()) != STATUS_ERROR) {
+ invocationCtx.getSingleCSID()) != STATUS_ERROR) {
results.setNumAffected(1);
results.setUserNote("CreateAndLinkLoanOutBatchJob created new Loanout: "
- +results.getPrimaryURICreated()+" with a link to the passed "+context.getDocType());
+ +results.getPrimaryURICreated()+" with a link to the passed "+invocationCtx.getDocType());
completionStatus = STATUS_COMPLETE;
}
- } else if(INVOCATION_MODE_LIST.equalsIgnoreCase(context.getMode())) {
- InvocationContext.ListCSIDs listWrapper = context.getListCSIDs();
+ } else if(INVOCATION_MODE_LIST.equalsIgnoreCase(invocationCtx.getMode())) {
+ InvocationContext.ListCSIDs listWrapper = invocationCtx.getListCSIDs();
List<String> csids = listWrapper.getCsid();
if(csids.size()==0) {
completionStatus = STATUS_ERROR;
if(completionStatus!=STATUS_ERROR) {
results.setNumAffected(nCreated);
results.setUserNote("CreateAndLinkLoanOutBatchJob created new Loanout: "
- +results.getPrimaryURICreated()+" with "+nCreated+" link(s) to "+context.getDocType());
+ +results.getPrimaryURICreated()+" with "+nCreated+" link(s) to "+invocationCtx.getDocType());
completionStatus = STATUS_COMPLETE;
}
}
+ "<subjectCsid>"+loanCSID+"</subjectCsid>"
+ "<subjectDocumentType>"+LOAN_DOCTYPE+"</subjectDocumentType>"
+ "<objectCsid>"+toCSID+"</objectCsid>"
- + "<objectDocumentType>"+context.getDocType()+"</objectDocumentType>"
+ + "<objectDocumentType>"+invocationCtx.getDocType()+"</objectDocumentType>"
+ "<relationshipType>"+RELATION_TYPE+"</relationshipType>"
+ "<predicateDisplayName>"+RELATION_PREDICATE_DISP+"</predicateDisplayName>"
+ "</ns2:relations_common></document>";
import org.collectionspace.services.batch.UriInfoImpl;
import org.collectionspace.services.client.AbstractCommonListUtils;
import org.collectionspace.services.client.CollectionObjectClient;
+import org.collectionspace.services.client.IClientQueryParams;
import org.collectionspace.services.client.IQueryManager;
import org.collectionspace.services.client.MovementClient;
import org.collectionspace.services.client.PoxPayloadOut;
import org.collectionspace.services.common.ResourceMap;
import org.collectionspace.services.common.api.RefNameUtils;
import org.collectionspace.services.common.api.Tools;
+import org.collectionspace.services.common.context.ServiceContext;
import org.collectionspace.services.common.invocable.InvocationResults;
import org.collectionspace.services.jaxb.AbstractCommonList;
+
import org.dom4j.DocumentException;
//import org.jboss.resteasy.specimpl.UriInfoImpl;
import org.jdom.Document;
Namespace.getNamespace(
COLLECTIONOBJECTS_COMMON_NAMESPACE_PREFIX,
COLLECTIONOBJECTS_COMMON_NAMESPACE_URI);
+ private static final int DEFAULT_PAGE_SIZE = 1000;
private final boolean EXCLUDE_DELETED = true;
private final String CLASSNAME = this.getClass().getSimpleName();
private final Logger logger = LoggerFactory.getLogger(this.getClass());
NuxeoBasedResource collectionObjectResource = (NuxeoBasedResource) resourcemap.get(CollectionObjectClient.SERVICE_NAME);
NuxeoBasedResource movementResource = (NuxeoBasedResource) resourcemap.get(MovementClient.SERVICE_NAME);
String computedCurrentLocation;
- int numUpdated = 0;
+ long numUpdated = 0;
+ long processed = 0;
+ long recordsToProcess = csids.size();
+ long logInterval = recordsToProcess / 10 + 2;
try {
// For each CollectionObject record
for (String collectionObjectCsid : csids) {
-
- // FIXME: Optionally set competition status here to
- // indicate what percentage of records have been processed.
+
+ // Log progress at INFO level
+ if (processed % logInterval == 0) {
+ logger.info(String.format("Recalculated computed location for %d of %d cataloging records.",
+ processed, recordsToProcess));
+ }
+ processed++;
// Skip over soft-deleted CollectionObject records
//
getResults().setNumAffected(numUpdated);
return getResults();
}
+
+ //
+ // Returns the number of distinct/unique CSID values in the list
+ //
+ private int getNumberOfDistinceRecords(AbstractCommonList abstractCommonList) {
+ Set<String> resultSet = new HashSet<String>();
+
+ for (AbstractCommonList.ListItem listItem : abstractCommonList.getListItem()) {
+ String csid = AbstractCommonListUtils.ListItemGetElementValue(listItem, CSID_ELEMENT_NAME);
+ if (!Tools.isBlank(csid)) {
+ resultSet.add(csid);
+ }
+ }
+
+ return resultSet.size();
+ }
private AbstractCommonList.ListItem getMostRecentMovement(AbstractCommonList relatedMovements) {
Set<String> alreadyProcessedMovementCsids = new HashSet<String>();
String updateDate;
String mostRecentLocationDate = "";
String comparisonUpdateDate = "";
+
+ //
+ // If there is only one related movement record, then return it as the most recent
+ // movement record -if it's current location element is not empty.
+ //
+ if (getNumberOfDistinceRecords(relatedMovements) == 1) {
+ mostRecentMovement = relatedMovements.getListItem().get(0);
+ currentLocation = AbstractCommonListUtils.ListItemGetElementValue(mostRecentMovement, CURRENT_LOCATION_ELEMENT_NAME);
+ if (Tools.isBlank(currentLocation)) {
+ mostRecentMovement = null;
+ }
+ return mostRecentMovement;
+ }
+
for (AbstractCommonList.ListItem movementListItem : relatedMovements.getListItem()) {
movementCsid = AbstractCommonListUtils.ListItemGetElementValue(movementListItem, CSID_ELEMENT_NAME);
if (Tools.isBlank(movementCsid)) {
}
}
+
return mostRecentMovement;
}
//
// Note: any such values must first be exposed in Movement list items,
// in turn via configuration in Services tenant bindings ("listResultsField").
- protected int updateCollectionObjectValues(NuxeoBasedResource collectionObjectResource,
- String collectionObjectCsid, AbstractCommonList.ListItem mostRecentMovement,
- ResourceMap resourcemap, int numUpdated)
+ protected long updateCollectionObjectValues(NuxeoBasedResource collectionObjectResource,
+ String collectionObjectCsid,
+ AbstractCommonList.ListItem mostRecentMovement,
+ ResourceMap resourcemap, long numUpdated)
throws DocumentException, URISyntaxException {
PoxPayloadOut collectionObjectPayload;
String computedCurrentLocation;
if (logger.isTraceEnabled()) {
logger.trace("Update payload: " + "\n" + collectionObjectUpdatePayload);
}
- byte[] response = collectionObjectResource.update(resourcemap, null, collectionObjectCsid,
+
+ UriInfo uriInfo = this.setupQueryParamForUpdateRecords(); // Determines if we'll updated the updateAt and updatedBy core values
+ byte[] response = collectionObjectResource.update(resourcemap, uriInfo, collectionObjectCsid,
collectionObjectUpdatePayload);
numUpdated++;
+
if (logger.isTraceEnabled()) {
logger.trace("Computed current location value for CollectionObject " + collectionObjectCsid
+ " was set to " + computedCurrentLocation);
URI uri = new URI(null, null, null, queryString, null);
return createUriInfo(uri.getRawQuery());
}
+
+ protected UriInfo setupQueryParamForUpdateRecords() throws URISyntaxException {
+ UriInfo result = null;
+
+ //
+ // Check first to see if we've got a query param. It will override any invocation context value
+ //
+ String updateCoreValues = (String) getServiceContext().getQueryParams().getFirst(IClientQueryParams.UPDATE_CORE_VALUES);
+ if (Tools.isBlank(updateCoreValues)) {
+ //
+ // Since there is no query param, let's check the invocation context
+ //
+ updateCoreValues = getInvocationContext().getUpdateCoreValues();
+ }
+
+ //
+ // If we found a value, then use it to create a query parameter
+ //
+ if (Tools.notBlank(updateCoreValues)) {
+ result = createUriInfo(IClientQueryParams.UPDATE_CORE_VALUES + "=" + updateCoreValues);
+ }
+
+ return result;
+ }
protected String getFieldElementValue(PoxPayloadOut payload, String partLabel, Namespace partNamespace, String fieldPath) {
String value = null;
uriInfo.getQueryParameters().add(WorkflowClient.WORKFLOW_QUERY_NONDELETED, Boolean.FALSE.toString());
return uriInfo;
}
+
+ private UriInfo addFilterForPageSize(UriInfo uriInfo, long startPage, long pageSize) throws URISyntaxException {
+ if (uriInfo == null) {
+ uriInfo = createUriInfo();
+ }
+ uriInfo.getQueryParameters().addFirst(IClientQueryParams.START_PAGE_PARAM, Long.toString(startPage));
+ uriInfo.getQueryParameters().addFirst(IClientQueryParams.PAGE_SIZE_PARAM, Long.toString(pageSize));
+
+ return uriInfo;
+ }
private AbstractCommonList getRecordsRelatedToCsid(NuxeoBasedResource resource, String csid,
String relationshipDirection, boolean excludeDeletedRecords) throws URISyntaxException {
}
return csids;
}
+
+ private void appendItemsToCsidsList(List<String> existingList, AbstractCommonList abstractCommonList) {
+ for (AbstractCommonList.ListItem listitem : abstractCommonList.getListItem()) {
+ existingList.add(AbstractCommonListUtils.ListItemGetCSID(listitem));
+ }
+ }
+
private List<String> getMemberCsidsFromGroup(String serviceName, String groupCsid) throws URISyntaxException, DocumentException {
ResourceMap resourcemap = getResourceMap();
NuxeoBasedResource collectionObjectResource = (NuxeoBasedResource) resourcemap.get(CollectionObjectClient.SERVICE_NAME);
UriInfo uriInfo = createUriInfo();
uriInfo = addFilterToExcludeSoftDeletedRecords(uriInfo);
- AbstractCommonList collectionObjects = collectionObjectResource.getList(uriInfo);
- List<String> noContextCsids = getCsidsList(collectionObjects);
+
+ boolean morePages = true;
+ long currentPage = 0;
+ long totalItems = 0;
+ long pageSize = DEFAULT_PAGE_SIZE;
+ List<String> noContextCsids = new ArrayList<String>();
+
+ while (morePages == true) {
+ uriInfo = addFilterForPageSize(uriInfo, currentPage, pageSize);
+ AbstractCommonList collectionObjects = collectionObjectResource.getList(uriInfo);
+ appendItemsToCsidsList(noContextCsids, collectionObjects);
+
+ if (collectionObjects.getItemsInPage() == pageSize) { // We know we're at the last page when the number of items returned in the last request is less than the page size.
+ currentPage++;
+ } else {
+ morePages = false;
+ }
+ }
+
return noContextCsids;
}
}
public static final String START_PAGE_PARAM = "pgNum";
public static final String ORDER_BY_PARAM = "sortBy";
public static final String IMPORT_TIMEOUT_PARAM = "impTimout";
-
+ public static final String UPDATE_CORE_VALUES = "updateCoreValues";
}
*/
public class Tools {
+ public static class NoRelatedRecordsException extends Exception {
+
+ }
+
private static final String PROPERTY_VAR_REGEX = "\\$\\{([A-Za-z0-9_\\.]+)\\}";
/** @return first glued to second with the separator string, at most one time - useful for appending paths.
return this.getTimeoutParam(uriInfo);
}
+ /**
+ * Returns TRUE unless the "recordUpdates" query param is set with a value of either "false", "FALSE", or "0"
+ * @return
+ */
+ @Override
+ public boolean shouldUpdateCoreValues() {
+ boolean recordUpdates = true;
+
+ MultivaluedMap<String, String> queryParams = getQueryParams();
+ String paramValue = queryParams.getFirst(IClientQueryParams.UPDATE_CORE_VALUES);
+ if (paramValue != null && paramValue.equalsIgnoreCase(Boolean.FALSE.toString())) { // Find our if the caller wants us to record updates
+ recordUpdates = false;
+ } else if (paramValue != null && paramValue.equals(Long.toString(0))) {
+ recordUpdates = false;
+ }
+
+ return recordUpdates;
+ }
+
/* (non-Javadoc)
* @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel()
*/
*/
public SecurityContext getSecurityContext();
+ /**
+ * Returns TRUE unless the "recordUpdates" query param is set with a value of either "false", "FALSE", or "0"
+ * @return
+ */
+ public boolean shouldUpdateCoreValues();
+
/**
* getTimeoutSecs();
*/
import org.collectionspace.services.common.invocable.InvocationContext;
import java.util.List;
import org.collectionspace.services.common.api.Tools;
+import org.collectionspace.services.common.context.ServiceContext;
/**
* Invocable defines an interface for invocable jobs (batch, reports, exports,
* Will only be called if getCompletionStatus() returns STATUS_ERROR.
*/
public InvocationError getErrorInfo();
+
+ /*
+ * Save a handle to the JAX-RS related service context
+ */
+ void setServiceContext(ServiceContext context);
+
+ ServiceContext getServiceContext();
+
+ InvocationContext getInvocationContext();
}
import org.apache.commons.lang.StringUtils;
import org.collectionspace.services.client.Profiler;
import org.collectionspace.services.client.CollectionSpaceClient;
+import org.collectionspace.services.client.IClientQueryParams;
import org.collectionspace.services.client.IQueryManager;
import org.collectionspace.services.client.IRelationsManager;
import org.collectionspace.services.client.PoxPayloadIn;
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.model.PropertyException;
import org.nuxeo.ecm.core.lifecycle.LifeCycle;
import org.nuxeo.ecm.core.lifecycle.LifeCycleService;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//
// Add updatedAt timestamp and updateBy user
//
- documentModel.setProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
- CollectionSpaceClient.COLLECTIONSPACE_CORE_UPDATED_AT, now);
- documentModel.setProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
- CollectionSpaceClient.COLLECTIONSPACE_CORE_UPDATED_BY, userId);
+ if (ctx.shouldUpdateCoreValues() == true) { // Ensure that our caller wants us to record this update
+ documentModel.setProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
+ CollectionSpaceClient.COLLECTIONSPACE_CORE_UPDATED_AT, now);
+ documentModel.setProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
+ CollectionSpaceClient.COLLECTIONSPACE_CORE_UPDATED_BY, userId);
+ } else {
+ logger.debug(String.format("Document with CSID=%s updated %s by user %s", documentModel.getName(), now, userId));
+ }
}
}
<xs:complexType>
<xs:sequence>
<xs:element name="mode" type="xs:string"/>
+ <xs:element name="updateCoreValues" type="xs:string"/>
<xs:element name="docType" type="xs:string"/>
<xs:element name="singleCSID" type="xs:string"/>
<xs:element name="groupCSID" type="xs:string"/>