import java.util.List;
import java.util.Set;
import javax.ws.rs.core.PathSegment;
-import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.collectionspace.services.batch.AbstractBatchInvocable;
import org.collectionspace.services.client.AbstractCommonListUtils;
COLLECTIONOBJECTS_COMMON_NAMESPACE_URI);
private final static String NONDELETED_QUERY_COMPONENT =
"&" + WorkflowClient.WORKFLOW_QUERY_NONDELETED + "=false";
- private PoxPayloadOut payloadOut;
private final String CLASSNAME = this.getClass().getSimpleName();
private final Logger logger = LoggerFactory.getLogger(this.getClass());
try {
List<String> csids = new ArrayList<String>();
+
+ // Build a list of CollectionObject records to process via this
+ // batch job, depending on the invocation mode requested.
if (requestIsForInvocationModeSingle()) {
String singleCsid = getInvocationContext().getSingleCSID();
if (Tools.isBlank(singleCsid)) {
// FIXME: Add code to invoke batch job on every active CollectionObject
}
- // Update the computed current location field for each CollectionObject
+ // Update the value of the computed current location field for each CollectionObject
setResults(updateComputedCurrentLocations(csids));
setCompletionStatus(STATUS_COMPLETE);
}
+ private InvocationResults updateComputedCurrentLocations(List<String> csids) {
+
+ ResourceMap resourcemap = getResourceMap();
+ ResourceBase collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
+ ResourceBase movementResource = resourcemap.get(MovementClient.SERVICE_NAME);
+ String computedCurrentLocation;
+ PoxPayloadOut payloadOut;
+ int numAffected = 0;
+
+ try {
+
+ // For each CollectionObject record
+ for (String collectionObjectCsid : csids) {
+
+ // Skip over soft-deleted CollectionObject records
+ if (isRecordDeleted(collectionObjectResource, collectionObjectCsid)) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Skipping soft-deleted CollectionObject record with CSID " + collectionObjectCsid);
+ }
+ continue;
+ }
+
+ // Get the Movement records related to this record
+ AbstractCommonList relatedMovements = getRelatedMovements(movementResource, collectionObjectCsid);
+
+ // Skip over CollectionObject records that have no related Movement records
+ if (relatedMovements.getListItem().isEmpty()) {
+ continue;
+ }
+
+ // Based on data in its related Movement records, compute the
+ // current location of this CollectionObject
+ computedCurrentLocation = computeCurrentLocation(relatedMovements);
+
+ // Skip over CollectionObject records where no computed current
+ // location value can be obtained from related Movement records.
+ //
+ // FIXME: Clarify: it ever necessary to 'unset' a computed
+ // current location value, by setting it to a null or empty value,
+ // if that value is no longer obtainable from related Movement records?
+ if (Tools.isBlank(computedCurrentLocation)) {
+ continue;
+ }
+
+ // Update the value of the computed current location field
+ // in the CollectionObject record
+ numAffected = updateComputedCurrentLocationValue(collectionObjectResource,
+ collectionObjectCsid, computedCurrentLocation, resourcemap, numAffected);
+ }
+
+ } catch (Exception e) {
+ String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage() + " ";
+ errMsg = errMsg + "Successfully updated " + numAffected + " CollectionObject record(s) prior to error.";
+ logger.error(errMsg);
+ setErrorResult(errMsg);
+ getResults().setNumAffected(numAffected);
+ return getResults();
+ }
+
+ logger.info("Updated computedCurrentLocation values in " + numAffected + " CollectionObject record(s).");
+ getResults().setNumAffected(numAffected);
+ return getResults();
+ }
+
+ private AbstractCommonList getRelatedMovements(ResourceBase movementResource, String csid)
+ throws URISyntaxException, DocumentException {
+
+ // Get movement records related to a record, specified by its CSID,
+ // where the record is the object of the relation
+ // FIXME: Get query string(s) from constant(s), where appropriate
+ String queryString = "rtObj=" + csid + NONDELETED_QUERY_COMPONENT;
+ UriInfo uriInfo = createRelatedRecordsUriInfo(queryString);
+ AbstractCommonList relatedMovements = movementResource.getList(uriInfo);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Identified " + relatedMovements.getTotalItems()
+ + " Movement record(s) related to the object CollectionObject record " + csid);
+ }
+
+ // Get movement records related to a record, specified by its CSID,
+ // where the record is the subject of the relation
+ // FIXME: Get query string(s) from constant(s), where appropriate
+ queryString = "rtSbj=" + csid + NONDELETED_QUERY_COMPONENT;
+ uriInfo = createRelatedRecordsUriInfo(queryString);
+ AbstractCommonList reverseRelatedMovements = movementResource.getList(uriInfo);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Identified " + reverseRelatedMovements.getTotalItems()
+ + " Movement record(s) related to the subject CollectionObject record " + csid);
+ }
+
+ // If the second list contains any related movement records,
+ // merge it into the first list
+ if (reverseRelatedMovements.getListItem().size() > 0) {
+ relatedMovements.getListItem().addAll(reverseRelatedMovements.getListItem());
+ }
+
+ if (logger.isTraceEnabled()) {
+ logger.trace("Identified a total of " + relatedMovements.getListItem().size()
+ + " Movement record(s) related to the subject CollectionObject record " + csid);
+ }
+
+ return relatedMovements;
+ }
+
+ private String computeCurrentLocation(AbstractCommonList relatedMovements) {
+ String computedCurrentLocation;
+ String movementCsid;
+ Set<String> alreadyProcessedMovementCsids = new HashSet<String>();
+ computedCurrentLocation = "";
+ String currentLocation;
+ String locationDate;
+ String mostRecentLocationDate = "";
+ for (AbstractCommonList.ListItem movementRecord : relatedMovements.getListItem()) {
+ movementCsid = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, CSID_ELEMENT_NAME);
+ if (Tools.isBlank(movementCsid)) {
+ continue;
+ }
+ // Avoid processing any related Movement record more than once,
+ // regardless of the directionality of its relation(s) to this
+ // CollectionObject record.
+ if (alreadyProcessedMovementCsids.contains(movementCsid)) {
+ continue;
+ } else {
+ alreadyProcessedMovementCsids.add(movementCsid);
+ }
+ locationDate = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, LOCATION_DATE_ELEMENT_NAME);
+ if (Tools.isBlank(locationDate)) {
+ continue;
+ }
+ currentLocation = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, CURRENT_LOCATION_ELEMENT_NAME);
+ if (Tools.isBlank(currentLocation)) {
+ continue;
+ }
+ if (logger.isTraceEnabled()) {
+ logger.trace("Location date value = " + locationDate);
+ logger.trace("Current location value = " + currentLocation);
+ }
+ // If this record's location date value is more recent than that of other
+ // Movement records processed so far, set the computed current location
+ // to its current location value.
+ //
+ // Assumes that all values for this element/field will be consistent ISO 8601
+ // date/time representations, each of which can be ordered via string comparison.
+ //
+ // If this is *not* the case, we can instead parse and convert these values
+ // to date/time objects.
+ if (locationDate.compareTo(mostRecentLocationDate) > 0) {
+ mostRecentLocationDate = locationDate;
+ // FIXME: Add optional validation here that the currentLocation value
+ // parses successfully as an item refName. (We might make that validation
+ // dependent on the value of a parameter passed in during batch job invocation.)
+ computedCurrentLocation = currentLocation;
+ }
+
+ }
+ return computedCurrentLocation;
+ }
+
+ private int updateComputedCurrentLocationValue(ResourceBase collectionObjectResource,
+ String collectionObjectCsid, String computedCurrentLocation, ResourceMap resourcemap, int numAffected)
+ throws DocumentException, URISyntaxException {
+ PoxPayloadOut collectionObjectPayload;
+ String objectNumber;
+
+ collectionObjectPayload = findByCsid(collectionObjectResource, collectionObjectCsid);
+ if (Tools.notBlank(collectionObjectPayload.toXML())) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Payload: " + "\n" + collectionObjectPayload);
+ }
+ objectNumber = getFieldElementValue(collectionObjectPayload,
+ COLLECTIONOBJECTS_COMMON_SCHEMA_NAME, COLLECTIONOBJECTS_COMMON_NAMESPACE,
+ OBJECT_NUMBER_ELEMENT_NAME);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Object number: " + objectNumber);
+ }
+ if (Tools.notBlank(objectNumber)) {
+ String collectionObjectUpdatePayload =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<document name=\"collectionobject\">"
+ + " <ns2:collectionobjects_common "
+ + " xmlns:ns2=\"http://collectionspace.org/services/collectionobject\">"
+ + " <objectNumber>" + objectNumber + "</objectNumber>"
+ + " <computedCurrentLocation>" + computedCurrentLocation + "</computedCurrentLocation>"
+ + " </ns2:collectionobjects_common>"
+ + "</document>";
+ if (logger.isTraceEnabled()) {
+ logger.trace("Update payload: " + "\n" + collectionObjectUpdatePayload);
+ }
+ byte[] response = collectionObjectResource.update(resourcemap, null, collectionObjectCsid,
+ collectionObjectUpdatePayload);
+ numAffected++;
+ if (logger.isTraceEnabled()) {
+ logger.trace("Computed current location value for CollectionObject " + collectionObjectCsid
+ + " was set to " + computedCurrentLocation);
+ }
+ }
+
+ }
+ return numAffected;
+ }
+
// #################################################################
// Ray's convenience methods from his AbstractBatchJob class for the
// UC Berkeley Botanical Garden v2.4 implementation.
boolean isDeleted = false;
byte[] workflowResponse = resource.getWorkflow(createUriInfo(), collectionObjectCsid);
if (workflowResponse != null) {
- payloadOut = new PoxPayloadOut(workflowResponse);
+ PoxPayloadOut payloadOut = new PoxPayloadOut(workflowResponse);
String workflowState =
getFieldElementValue(payloadOut, WORKFLOW_COMMON_SCHEMA_NAME,
WORKFLOW_COMMON_NAMESPACE, LIFECYCLE_STATE_ELEMENT_NAME);
}
return isDeleted;
}
-
- private InvocationResults updateComputedCurrentLocations(List<String> csids) {
-
- ResourceMap resourcemap = getResourceMap();
- ResourceBase collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
- ResourceBase movementResource = resourcemap.get(MovementClient.SERVICE_NAME);
- PoxPayloadOut collectionObjectPayload;
- String computedCurrentLocation;
- String objectNumber;
- String movementCsid;
- PoxPayloadOut payloadOut;
- String queryString;
- int numAffected = 0;
-
- try {
-
- // For each CollectionObject record
- for (String collectionObjectCsid : csids) {
-
- // Skip over soft-deleted CollectionObject records
- if (isRecordDeleted(collectionObjectResource, collectionObjectCsid)) {
- if (logger.isTraceEnabled()) {
- logger.trace("Skipping soft-deleted CollectionObject record with CSID " + collectionObjectCsid);
- }
- continue;
- }
-
- // Get the movement records related to this record
-
- // Get movement records related to this record where the CollectionObject
- // record is the subject of the relation
- // FIXME: Get query string(s) from constant(s), where appropriate
- queryString = "rtObj=" + collectionObjectCsid + NONDELETED_QUERY_COMPONENT;
- UriInfo uriInfo = createRelatedRecordsUriInfo(queryString);
-
- AbstractCommonList relatedMovements = movementResource.getList(uriInfo);
- if (logger.isTraceEnabled()) {
- logger.trace("Identified " + relatedMovements.getTotalItems()
- + " Movement record(s) related to the object CollectionObject record " + collectionObjectCsid);
- }
-
- // Get movement records related to this record where the CollectionObject
- // record is the object of the relation
- // FIXME: Get query string(s) from constant(s), where appropriate
- queryString = "rtSbj=" + collectionObjectCsid + NONDELETED_QUERY_COMPONENT;
- uriInfo = createRelatedRecordsUriInfo(queryString);
-
- AbstractCommonList reverseRelatedMovements = movementResource.getList(uriInfo);
- if (logger.isTraceEnabled()) {
- logger.trace("Identified " + reverseRelatedMovements.getTotalItems()
- + " Movement record(s) related to the subject CollectionObject record " + collectionObjectCsid);
- }
-
- if ((relatedMovements.getTotalItems() == 0) && reverseRelatedMovements.getTotalItems() == 0) {
- continue;
- }
-
- // Merge the two lists of related movement records
- relatedMovements.getListItem().addAll(reverseRelatedMovements.getListItem());
-
- if (logger.isTraceEnabled()) {
- logger.trace("Identified a total of " + relatedMovements.getListItem().size()
- + " Movement record(s) related to the subject CollectionObject record " + collectionObjectCsid);
- }
-
- // Get the latest movement record from among those, and extract
- // its current location value
- Set<String> alreadyProcessedMovementCsids = new HashSet<String>();
- computedCurrentLocation = "";
- String currentLocation;
- String locationDate;
- String mostRecentLocationDate = "";
- for (AbstractCommonList.ListItem movementRecord : relatedMovements.getListItem()) {
- movementCsid = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, CSID_ELEMENT_NAME);
- if (Tools.isBlank(movementCsid)) {
- continue;
- }
- // Avoid processing any related Movement record more than once,
- // regardless of the directionality of its relation(s) to this
- // CollectionObject record.
- if (alreadyProcessedMovementCsids.contains(movementCsid)) {
- continue;
- } else {
- alreadyProcessedMovementCsids.add(movementCsid);
- }
- locationDate = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, LOCATION_DATE_ELEMENT_NAME);
- if (Tools.isBlank(locationDate)) {
- continue;
- }
- currentLocation = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, CURRENT_LOCATION_ELEMENT_NAME);
- if (Tools.isBlank(currentLocation)) {
- continue;
- }
- if (logger.isTraceEnabled()) {
- logger.trace("Location date value = " + locationDate);
- logger.trace("Current location value = " + currentLocation);
- }
- // Assumes that all values for this element/field will be consistent ISO 8601
- // date/time representations, each of which can be ordered via string comparison.
- //
- // If this is *not* the case, we can instead parse and convert these values
- // to date/time objects.
- if (locationDate.compareTo(mostRecentLocationDate) > 0) {
- mostRecentLocationDate = locationDate;
- // FIXME: Add optional validation here that the currentLocation value
- // parses successfully as an item refName
- computedCurrentLocation = currentLocation;
- }
-
- }
-
- // Update the computed current location value in the CollectionObject record
- collectionObjectPayload = findByCsid(collectionObjectResource, collectionObjectCsid);
- if (Tools.notBlank(collectionObjectPayload.toXML())) {
- if (logger.isTraceEnabled()) {
- logger.trace("Payload: " + "\n" + collectionObjectPayload);
- }
- objectNumber = getFieldElementValue(collectionObjectPayload,
- COLLECTIONOBJECTS_COMMON_SCHEMA_NAME, COLLECTIONOBJECTS_COMMON_NAMESPACE,
- OBJECT_NUMBER_ELEMENT_NAME);
- if (logger.isTraceEnabled()) {
- logger.trace("Object number: " + objectNumber);
- }
- if (Tools.notBlank(objectNumber)) {
- String collectionObjectUpdatePayload =
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
- + "<document name=\"collectionobject\">"
- + " <ns2:collectionobjects_common "
- + " xmlns:ns2=\"http://collectionspace.org/services/collectionobject\">"
- + " <objectNumber>" + objectNumber + "</objectNumber>"
- + " <computedCurrentLocation>" + computedCurrentLocation + "</computedCurrentLocation>"
- + " </ns2:collectionobjects_common>"
- + "</document>";
- if (logger.isTraceEnabled()) {
- logger.trace("Update payload: " + "\n" + collectionObjectUpdatePayload);
- }
- byte[] response = collectionObjectResource.update(resourcemap, null, collectionObjectCsid,
- collectionObjectUpdatePayload);
- numAffected++;
- if (logger.isTraceEnabled()) {
- logger.trace("Computed current location value for CollectionObject " + collectionObjectCsid
- + " was set to " + computedCurrentLocation);
- }
- }
-
- }
- }
- } catch (Exception e) {
- String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage() + " ";
- errMsg = errMsg + "Successfully updated " + numAffected + " CollectionObject record(s) prior to error.";
- logger.error(errMsg);
- setErrorResult(errMsg);
- getResults().setNumAffected(numAffected);
- return getResults();
- }
-
- logger.info("Updated computedCurrentLocation values in " + numAffected + " CollectionObject record(s).");
- getResults().setNumAffected(numAffected);
- return getResults();
- }
}