]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
590f0cac8f434e23e0fb03f83c300a23565a833b
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.batch.nuxeo;
2
3 import java.io.StringReader;
4 import java.net.URI;
5 import java.net.URISyntaxException;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collections;
9 import java.util.HashSet;
10 import java.util.List;
11 import java.util.Set;
12 import javax.ws.rs.core.PathSegment;
13 import javax.ws.rs.core.UriInfo;
14 import org.collectionspace.services.batch.AbstractBatchInvocable;
15 import org.collectionspace.services.client.AbstractCommonListUtils;
16 import org.collectionspace.services.client.CollectionObjectClient;
17 import org.collectionspace.services.client.IQueryManager;
18 import org.collectionspace.services.client.MovementClient;
19 import org.collectionspace.services.client.PoxPayloadOut;
20 import org.collectionspace.services.client.workflow.WorkflowClient;
21 import org.collectionspace.services.common.ResourceBase;
22 import org.collectionspace.services.common.ResourceMap;
23 import org.collectionspace.services.common.api.Tools;
24 import org.collectionspace.services.common.invocable.InvocationResults;
25 import org.collectionspace.services.jaxb.AbstractCommonList;
26 import org.dom4j.DocumentException;
27 import org.jboss.resteasy.specimpl.UriInfoImpl;
28 import org.jdom.Document;
29 import org.jdom.Element;
30 import org.jdom.Namespace;
31 import org.jdom.input.SAXBuilder;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 public class UpdateObjectLocationBatchJob extends AbstractBatchInvocable {
36
37     // FIXME: Where appropriate, get from existing constants rather than local declarations
38     private final static String COMPUTED_CURRENT_LOCATION_ELEMENT_NAME = "computedCurrentLocation";
39     private final static String CSID_ELEMENT_NAME = "csid";
40     private final static String CURRENT_LOCATION_ELEMENT_NAME = "currentLocation";
41     private final static String LIFECYCLE_STATE_ELEMENT_NAME = "currentLifeCycleState";
42     private final static String LOCATION_DATE_ELEMENT_NAME = "locationDate";
43     private final static String OBJECT_NUMBER_ELEMENT_NAME = "objectNumber";
44     private final static String UPDATE_DATE_ELEMENT_NAME = "updatedAt";
45     private final static String WORKFLOW_COMMON_SCHEMA_NAME = "workflow_common";
46     private final static String WORKFLOW_COMMON_NAMESPACE_PREFIX = "ns2";
47     private final static String WORKFLOW_COMMON_NAMESPACE_URI =
48             "http://collectionspace.org/services/workflow";
49     private final static Namespace WORKFLOW_COMMON_NAMESPACE =
50             Namespace.getNamespace(
51             WORKFLOW_COMMON_NAMESPACE_PREFIX,
52             WORKFLOW_COMMON_NAMESPACE_URI);
53     private final static String COLLECTIONOBJECTS_COMMON_SCHEMA_NAME = "collectionobjects_common";
54     private final static String COLLECTIONOBJECTS_COMMON_NAMESPACE_PREFIX = "ns2";
55     private final static String COLLECTIONOBJECTS_COMMON_NAMESPACE_URI =
56             "http://collectionspace.org/services/collectionobject";
57     private final static Namespace COLLECTIONOBJECTS_COMMON_NAMESPACE =
58             Namespace.getNamespace(
59             COLLECTIONOBJECTS_COMMON_NAMESPACE_PREFIX,
60             COLLECTIONOBJECTS_COMMON_NAMESPACE_URI);
61     private final boolean EXCLUDE_DELETED = true;
62     private final String CLASSNAME = this.getClass().getSimpleName();
63     private final Logger logger = LoggerFactory.getLogger(this.getClass());
64
65     // Initialization tasks
66     public UpdateObjectLocationBatchJob() {
67         setSupportedInvocationModes(Arrays.asList(INVOCATION_MODE_SINGLE, INVOCATION_MODE_LIST,
68                 INVOCATION_MODE_GROUP, INVOCATION_MODE_NO_CONTEXT));
69     }
70
71     /**
72      * The main work logic of the batch job. Will be called after setContext.
73      */
74     @Override
75     public void run() {
76
77         setCompletionStatus(STATUS_MIN_PROGRESS);
78
79         try {
80
81             List<String> csids = new ArrayList<String>();
82
83             // Build a list of CollectionObject records to process via this
84             // batch job, depending on the invocation mode requested.
85             if (requestIsForInvocationModeSingle()) {
86                 String singleCsid = getInvocationContext().getSingleCSID();
87                 if (Tools.isBlank(singleCsid)) {
88                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT);
89                 } else {
90                     csids.add(singleCsid);
91                 }
92             } else if (requestIsForInvocationModeList()) {
93                 List<String> listCsids = getListCsids();
94                 if (listCsids.isEmpty()) {
95                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT);
96                 }
97                 csids.addAll(listCsids);
98             } else if (requestIsForInvocationModeGroup()) {
99                 String groupCsid = getInvocationContext().getGroupCSID();
100                 if (Tools.isBlank(groupCsid)) {
101                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT);
102                 }
103                 List<String> groupMemberCsids = getMemberCsidsFromGroup(CollectionObjectClient.SERVICE_NAME, groupCsid);
104                 if (groupMemberCsids.isEmpty()) {
105                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT);
106                 }
107                 csids.addAll(groupMemberCsids);
108             } else if (requestIsForInvocationModeNoContext()) {
109                 List<String> noContextCsids = getNoContextCsids();
110                 if (noContextCsids.isEmpty()) {
111                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT);
112                 }
113                 csids.addAll(noContextCsids);
114             }
115             if (logger.isInfoEnabled()) {
116                 logger.info("Identified " + csids.size() + " total CollectionObject(s) to be processed via the " + CLASSNAME + " batch job");
117             }
118
119             // Update the value of the computed current location field for each CollectionObject
120             setResults(updateComputedCurrentLocations(csids));
121             setCompletionStatus(STATUS_COMPLETE);
122
123         } catch (Exception e) {
124             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage();
125             setErrorResult(errMsg);
126         }
127
128     }
129
130     private InvocationResults updateComputedCurrentLocations(List<String> csids) {
131         ResourceMap resourcemap = getResourceMap();
132         ResourceBase collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
133         ResourceBase movementResource = resourcemap.get(MovementClient.SERVICE_NAME);
134         String computedCurrentLocation;
135         int numUpdated = 0;
136
137         try {
138
139             // For each CollectionObject record
140             for (String collectionObjectCsid : csids) {
141
142                 // FIXME: Optionally set competition status here to
143                 // indicate what percentage of records have been processed.
144
145                 // Skip over soft-deleted CollectionObject records
146                 //
147                 // (Invocations using the 'no context' mode have already
148                 // filtered out soft-deleted records.)
149                 if (!requestIsForInvocationModeNoContext()) {
150                     if (isRecordDeleted(collectionObjectResource, collectionObjectCsid)) {
151                         if (logger.isTraceEnabled()) {
152                             logger.trace("Skipping soft-deleted CollectionObject record with CSID " + collectionObjectCsid);
153                         }
154                         continue;
155                     }
156                 }
157                 // Get the Movement records related to this CollectionObject record
158                 AbstractCommonList relatedMovements =
159                         getRelatedRecords(movementResource, collectionObjectCsid, EXCLUDE_DELETED);
160                 // Skip over CollectionObject records that have no related Movement records
161                 if (relatedMovements.getListItem().isEmpty()) {
162                     continue;
163                 }
164                 // Get the most recent 'suitable' Movement record, one which
165                 // contains both a location date and a current location value
166                 AbstractCommonList.ListItem mostRecentMovement = getMostRecentMovement(relatedMovements);
167                 // Skip over CollectionObject records where no suitable
168                 // most recent Movement record can be identified.
169                 //
170                 // FIXME: Clarify: it ever necessary to 'unset' a computed
171                 // current location value, by setting it to a null or empty value,
172                 // if that value is no longer obtainable from related Movement
173                 // records, if any?
174                 if (mostRecentMovement == null) {
175                     continue;
176                 }
177                 // Update the value of the computed current location field
178                 // (and, via subclasses, this and/or other relevant fields)
179                 // in the CollectionObject record
180                 numUpdated = updateCollectionObjectValues(collectionObjectResource,
181                         collectionObjectCsid, mostRecentMovement, resourcemap, numUpdated);
182             }
183
184         } catch (Exception e) {
185             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage() + " ";
186             errMsg = errMsg + "Successfully updated " + numUpdated + " CollectionObject record(s) prior to error.";
187             logger.error(errMsg);
188             setErrorResult(errMsg);
189             getResults().setNumAffected(numUpdated);
190             return getResults();
191         }
192
193         logger.info("Updated computedCurrentLocation values in " + numUpdated + " CollectionObject record(s).");
194         getResults().setNumAffected(numUpdated);
195         return getResults();
196     }
197
198     private AbstractCommonList.ListItem getMostRecentMovement(AbstractCommonList relatedMovements) {
199         Set<String> alreadyProcessedMovementCsids = new HashSet<String>();
200         AbstractCommonList.ListItem mostRecentMovement = null;
201         String movementCsid;
202         String currentLocation;
203         String locationDate;
204         String updateDate;
205         String mostRecentLocationDate = "";
206         String comparisonUpdateDate = "";
207         for (AbstractCommonList.ListItem movementListItem : relatedMovements.getListItem()) {
208             movementCsid = AbstractCommonListUtils.ListItemGetElementValue(movementListItem, CSID_ELEMENT_NAME);
209             if (Tools.isBlank(movementCsid)) {
210                 continue;
211             }
212             // Skip over any duplicates in the list, such as records that might
213             // appear as the subject of one relation record and the object of
214             // its reciprocal relation record
215             if (alreadyProcessedMovementCsids.contains(movementCsid)) {
216                 continue;
217             } else {
218                 alreadyProcessedMovementCsids.add(movementCsid);
219             }
220             locationDate = AbstractCommonListUtils.ListItemGetElementValue(movementListItem, LOCATION_DATE_ELEMENT_NAME);
221             if (Tools.isBlank(locationDate)) {
222                 continue;
223             }
224             updateDate = AbstractCommonListUtils.ListItemGetElementValue(movementListItem, UPDATE_DATE_ELEMENT_NAME);
225             if (Tools.isBlank(updateDate)) {
226                 continue;
227             }
228             currentLocation = AbstractCommonListUtils.ListItemGetElementValue(movementListItem, CURRENT_LOCATION_ELEMENT_NAME);
229             if (Tools.isBlank(currentLocation)) {
230                 continue;
231             }
232             // FIXME: Add optional validation here that this Movement record's
233             // currentLocation value parses successfully as an item refName,
234             // before identifying that record as the most recent Movement.
235             // Consider making this optional validation, in turn dependent on the
236             // value of a parameter passed in during batch job invocation.
237             if (logger.isTraceEnabled()) {
238                 logger.trace("Location date value = " + locationDate);
239                 logger.trace("Update date value = " + updateDate);
240                 logger.trace("Current location value = " + currentLocation);
241             }
242             // If this record's location date value is more recent than that of other
243             // Movement records processed so far, set the current Movement record
244             // as the most recent Movement.
245             //
246             // The following comparison assumes that all values for this element/field
247             // will be consistent ISO 8601 date/time representations, each of which can
248             // be ordered via string comparison.
249             //
250             // If this is *not* the case, we should instead parse and convert these values
251             // to date/time objects.
252             if (locationDate.compareTo(mostRecentLocationDate) > 0) {
253                 mostRecentLocationDate = locationDate;
254                 mostRecentMovement = movementListItem;
255                 comparisonUpdateDate = updateDate;
256             } else if (locationDate.compareTo(mostRecentLocationDate) == 0) {
257                 // If the two location dates match, then use a tiebreaker
258                 if (updateDate.compareTo(comparisonUpdateDate) > 0) {
259                     // The most recent location date value doesn't need to be
260                     // updated here, as the two records' values are identical
261                     mostRecentMovement = movementListItem;
262                     comparisonUpdateDate = updateDate;
263                 }
264             }
265
266         }
267         return mostRecentMovement;
268     }
269
270     // This method can be overridden and extended to update a custom set of
271     // values in the CollectionObject record by pulling in values from its
272     // most recent related Movement record.
273     //
274     // Note: any such values must first be exposed in Movement list items,
275     // in turn via configuration in Services tenant bindings ("listResultsField").
276     protected int updateCollectionObjectValues(ResourceBase collectionObjectResource,
277             String collectionObjectCsid, AbstractCommonList.ListItem mostRecentMovement,
278             ResourceMap resourcemap, int numUpdated)
279             throws DocumentException, URISyntaxException {
280         PoxPayloadOut collectionObjectPayload;
281         String computedCurrentLocation;
282         String objectNumber;
283         String previousComputedCurrentLocation;
284
285         collectionObjectPayload = findByCsid(collectionObjectResource, collectionObjectCsid);
286         if (Tools.isBlank(collectionObjectPayload.toXML())) {
287             return numUpdated;
288         } else {
289             if (logger.isTraceEnabled()) {
290                 logger.trace("Payload: " + "\n" + collectionObjectPayload);
291             }
292         }
293         // Perform the update only if the computed current location value will change
294         // as a result of the update
295         computedCurrentLocation =
296                 AbstractCommonListUtils.ListItemGetElementValue(mostRecentMovement, CURRENT_LOCATION_ELEMENT_NAME);
297         previousComputedCurrentLocation = getFieldElementValue(collectionObjectPayload,
298                 COLLECTIONOBJECTS_COMMON_SCHEMA_NAME, COLLECTIONOBJECTS_COMMON_NAMESPACE,
299                 COMPUTED_CURRENT_LOCATION_ELEMENT_NAME);
300         if (!shouldUpdateLocation(previousComputedCurrentLocation, computedCurrentLocation)) {
301             return numUpdated;
302         }
303     
304         // Perform the update only if there is a non-blank object number available.
305         //
306         // In the default CollectionObject validation handler, the object number
307         // is a required field and its (non-blank) value must be present in update
308         // payloads to successfully perform an update.
309         objectNumber = getFieldElementValue(collectionObjectPayload,
310                 COLLECTIONOBJECTS_COMMON_SCHEMA_NAME, COLLECTIONOBJECTS_COMMON_NAMESPACE,
311                 OBJECT_NUMBER_ELEMENT_NAME);
312         if (logger.isTraceEnabled()) {
313             logger.trace("Object number: " + objectNumber);
314         }
315         // FIXME: Consider making the requirement that a non-blank object number
316         // be present dependent on the value of a parameter passed in during
317         // batch job invocation, as some implementations may have turned off that
318         // validation requirement.
319         if (Tools.isBlank(objectNumber)) {
320             return numUpdated;
321         }
322
323         // Update the location.
324         // (Updated location values can legitimately be blank, to 'null out' existing locations.)  
325         if (computedCurrentLocation == null) {
326             computedCurrentLocation = "";
327         }
328
329         String collectionObjectUpdatePayload =
330                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
331                 + "<document name=\"collectionobject\">"
332                 + "  <ns2:collectionobjects_common "
333                 + "      xmlns:ns2=\"http://collectionspace.org/services/collectionobject\">"
334                 + "    <objectNumber>" + objectNumber + "</objectNumber>"
335                 + "    <computedCurrentLocation>" + computedCurrentLocation + "</computedCurrentLocation>"
336                 + "  </ns2:collectionobjects_common>"
337                 + "</document>";
338         if (logger.isTraceEnabled()) {
339             logger.trace("Update payload: " + "\n" + collectionObjectUpdatePayload);
340         }
341         byte[] response = collectionObjectResource.update(resourcemap, null, collectionObjectCsid,
342                 collectionObjectUpdatePayload);
343         numUpdated++;
344         if (logger.isTraceEnabled()) {
345             logger.trace("Computed current location value for CollectionObject " + collectionObjectCsid
346                     + " was set to " + computedCurrentLocation);
347
348         }
349         return numUpdated;
350     }
351     
352     protected boolean shouldUpdateLocation(String previousLocation, String currentLocation) {
353         boolean shouldUpdate = true;
354         if (Tools.isBlank(previousLocation) && Tools.isBlank(currentLocation)) {
355             shouldUpdate = false;
356         } else if (Tools.notBlank(previousLocation) && previousLocation.equals(currentLocation)) {
357             shouldUpdate = false;
358         }
359         return shouldUpdate;
360     }
361
362     // #################################################################
363     // Ray Lee's convenience methods from his AbstractBatchJob class for the
364     // UC Berkeley Botanical Garden v2.4 implementation.
365     // #################################################################
366     protected PoxPayloadOut findByCsid(String serviceName, String csid) throws URISyntaxException, DocumentException {
367         ResourceBase resource = getResourceMap().get(serviceName);
368         return findByCsid(resource, csid);
369     }
370
371     protected PoxPayloadOut findByCsid(ResourceBase resource, String csid) throws URISyntaxException, DocumentException {
372         byte[] response = resource.get(null, createUriInfo(), csid);
373         PoxPayloadOut payload = new PoxPayloadOut(response);
374         return payload;
375     }
376
377     protected UriInfo createUriInfo() throws URISyntaxException {
378         return createUriInfo("");
379     }
380
381     protected UriInfo createUriInfo(String queryString) throws URISyntaxException {
382         URI absolutePath = new URI("");
383         URI baseUri = new URI("");
384         return new UriInfoImpl(absolutePath, baseUri, "", queryString, Collections.<PathSegment>emptyList());
385     }
386
387     // #################################################################
388     // Other convenience methods
389     // #################################################################
390     protected UriInfo createRelatedRecordsUriInfo(String queryString) throws URISyntaxException {
391         URI uri = new URI(null, null, null, queryString, null);
392         return createUriInfo(uri.getRawQuery());
393     }
394
395     protected String getFieldElementValue(PoxPayloadOut payload, String partLabel, Namespace partNamespace, String fieldPath) {
396         String value = null;
397         SAXBuilder builder = new SAXBuilder();
398         try {
399             Document document = builder.build(new StringReader(payload.toXML()));
400             Element root = document.getRootElement();
401             // The part element is always expected to have an explicit namespace.
402             Element part = root.getChild(partLabel, partNamespace);
403             // Try getting the field element both with and without a namespace.
404             // Even though a field element that lacks a namespace prefix
405             // may yet inherit its namespace from a parent, JDOM may require that
406             // the getChild() call be made without a namespace.
407             Element field = part.getChild(fieldPath, partNamespace);
408             if (field == null) {
409                 field = part.getChild(fieldPath);
410             }
411             if (field != null) {
412                 value = field.getText();
413             }
414         } catch (Exception e) {
415             logger.error("Error getting value from field path " + fieldPath
416                     + " in schema part " + partLabel);
417             return null;
418         }
419         return value;
420     }
421
422     private boolean isRecordDeleted(ResourceBase resource, String collectionObjectCsid)
423             throws URISyntaxException, DocumentException {
424         boolean isDeleted = false;
425         byte[] workflowResponse = resource.getWorkflow(createUriInfo(), collectionObjectCsid);
426         if (workflowResponse != null) {
427             PoxPayloadOut payloadOut = new PoxPayloadOut(workflowResponse);
428             String workflowState =
429                     getFieldElementValue(payloadOut, WORKFLOW_COMMON_SCHEMA_NAME,
430                     WORKFLOW_COMMON_NAMESPACE, LIFECYCLE_STATE_ELEMENT_NAME);
431             if (Tools.notBlank(workflowState) && workflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
432                 isDeleted = true;
433             }
434         }
435         return isDeleted;
436     }
437
438     private UriInfo addFilterToExcludeSoftDeletedRecords(UriInfo uriInfo) throws URISyntaxException {
439         if (uriInfo == null) {
440             uriInfo = createUriInfo();
441         }
442         uriInfo.getQueryParameters().add(WorkflowClient.WORKFLOW_QUERY_NONDELETED, Boolean.FALSE.toString());
443         return uriInfo;
444     }
445
446     private AbstractCommonList getRecordsRelatedToCsid(ResourceBase resource, String csid,
447             String relationshipDirection, boolean excludeDeletedRecords) throws URISyntaxException {
448         UriInfo uriInfo = createUriInfo();
449         uriInfo.getQueryParameters().add(relationshipDirection, csid);
450         if (excludeDeletedRecords) {
451             uriInfo = addFilterToExcludeSoftDeletedRecords(uriInfo);
452         }
453         // The 'resource' type used here identifies the record type of the
454         // related records to be retrieved
455         AbstractCommonList relatedRecords = resource.getList(uriInfo);
456         if (logger.isTraceEnabled()) {
457             logger.trace("Identified " + relatedRecords.getTotalItems()
458                     + " record(s) related to the object record via direction " + relationshipDirection + " with CSID " + csid);
459         }
460         return relatedRecords;
461     }
462
463     /**
464      * Returns the records of a specified type that are related to a specified
465      * record, where that record is the object of the relation.
466      *
467      * @param resource a resource. The type of this resource determines the type
468      * of related records that are returned.
469      * @param csid a CSID identifying a record
470      * @param excludeDeletedRecords true if 'soft-deleted' records should be
471      * excluded from results; false if those records should be included
472      * @return a list of records of a specified type, related to a specified
473      * record
474      * @throws URISyntaxException
475      */
476     private AbstractCommonList getRecordsRelatedToObjectCsid(ResourceBase resource, String csid, boolean excludeDeletedRecords) throws URISyntaxException {
477         return getRecordsRelatedToCsid(resource, csid, IQueryManager.SEARCH_RELATED_TO_CSID_AS_OBJECT, excludeDeletedRecords);
478     }
479
480     /**
481      * Returns the records of a specified type that are related to a specified
482      * record, where that record is the subject of the relation.
483      *
484      * @param resource a resource. The type of this resource determines the type
485      * of related records that are returned.
486      * @param csid a CSID identifying a record
487      * @param excludeDeletedRecords true if 'soft-deleted' records should be
488      * excluded from results; false if those records should be included
489      * @return a list of records of a specified type, related to a specified
490      * record
491      * @throws URISyntaxException
492      */
493     private AbstractCommonList getRecordsRelatedToSubjectCsid(ResourceBase resource, String csid, boolean excludeDeletedRecords) throws URISyntaxException {
494         return getRecordsRelatedToCsid(resource, csid, IQueryManager.SEARCH_RELATED_TO_CSID_AS_SUBJECT, excludeDeletedRecords);
495     }
496
497     private AbstractCommonList getRelatedRecords(ResourceBase resource, String csid, boolean excludeDeletedRecords)
498             throws URISyntaxException, DocumentException {
499         AbstractCommonList relatedRecords = new AbstractCommonList();
500         AbstractCommonList recordsRelatedToObjectCSID = getRecordsRelatedToObjectCsid(resource, csid, excludeDeletedRecords);
501         AbstractCommonList recordsRelatedToSubjectCSID = getRecordsRelatedToSubjectCsid(resource, csid, excludeDeletedRecords);
502         // If either list contains any related records, merge in its items
503         if (recordsRelatedToObjectCSID.getListItem().size() > 0) {
504             relatedRecords.getListItem().addAll(recordsRelatedToObjectCSID.getListItem());
505         }
506         if (recordsRelatedToSubjectCSID.getListItem().size() > 0) {
507             relatedRecords.getListItem().addAll(recordsRelatedToSubjectCSID.getListItem());
508         }
509         if (logger.isTraceEnabled()) {
510             logger.trace("Identified a total of " + relatedRecords.getListItem().size()
511                     + " record(s) related to the record with CSID " + csid);
512         }
513         return relatedRecords;
514     }
515
516     private List<String> getCsidsList(AbstractCommonList list) {
517         List<String> csids = new ArrayList<String>();
518         for (AbstractCommonList.ListItem listitem : list.getListItem()) {
519             csids.add(AbstractCommonListUtils.ListItemGetCSID(listitem));
520         }
521         return csids;
522     }
523
524     private List<String> getMemberCsidsFromGroup(String serviceName, String groupCsid) throws URISyntaxException, DocumentException {
525         ResourceMap resourcemap = getResourceMap();
526         ResourceBase resource = resourcemap.get(serviceName);
527         return getMemberCsidsFromGroup(resource, groupCsid);
528     }
529
530     private List<String> getMemberCsidsFromGroup(ResourceBase resource, String groupCsid) throws URISyntaxException, DocumentException {
531         // The 'resource' type used here identifies the record type of the
532         // related records to be retrieved
533         AbstractCommonList relatedRecords =
534                 getRelatedRecords(resource, groupCsid, EXCLUDE_DELETED);
535         List<String> memberCsids = getCsidsList(relatedRecords);
536         return memberCsids;
537     }
538
539     private List<String> getNoContextCsids() throws URISyntaxException {
540         ResourceMap resourcemap = getResourceMap();
541         ResourceBase collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
542         UriInfo uriInfo = createUriInfo();
543         uriInfo = addFilterToExcludeSoftDeletedRecords(uriInfo);
544         AbstractCommonList collectionObjects = collectionObjectResource.getList(uriInfo);
545         List<String> noContextCsids = getCsidsList(collectionObjects);
546         return noContextCsids;
547     }
548 }