]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
e7f721f1d78f57c4680b77795b65989af0830a9b
[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.MovementClient;
18 import org.collectionspace.services.client.workflow.WorkflowClient;
19 import org.collectionspace.services.client.PoxPayloadOut;
20 import org.collectionspace.services.common.ResourceBase;
21 import org.collectionspace.services.common.ResourceMap;
22 import org.collectionspace.services.common.api.Tools;
23 import org.collectionspace.services.common.invocable.InvocationContext;
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 COLLECTIONOBJECTS_COMMON_SCHEMA_NAME = "collectionobjects_common";
39     private final static String CSID_ELEMENT_NAME = "csid";
40     private final static String CURRENT_LOCATION_ELEMENT_NAME = "currentLocation";
41     private final static String LOCATION_DATE_ELEMENT_NAME = "locationDate";
42     private final static String OBJECT_NUMBER_ELEMENT_NAME = "objectNumber";
43     private final static String COLLECTIONOBJECTS_COMMON_NAMESPACE_PREFIX = "ns2";
44     private final static String COLLECTIONOBJECTS_COMMON_NAMESPACE_URI =
45             "http://collectionspace.org/services/collectionobject";
46     private final static Namespace COLLECTIONOBJECTS_COMMON_NAMESPACE =
47             Namespace.getNamespace(
48             COLLECTIONOBJECTS_COMMON_NAMESPACE_PREFIX,
49             COLLECTIONOBJECTS_COMMON_NAMESPACE_URI);
50     private final String CLASSNAME = this.getClass().getSimpleName();
51     private final Logger logger = LoggerFactory.getLogger(this.getClass());
52
53     // Initialization tasks
54     public UpdateObjectLocationBatchJob() {
55         setSupportedInvocationModes(Arrays.asList(INVOCATION_MODE_SINGLE, INVOCATION_MODE_LIST));
56     }
57
58     /**
59      * The main work logic of the batch job. Will be called after setContext.
60      */
61     @Override
62     public void run() {
63
64         setCompletionStatus(STATUS_MIN_PROGRESS);
65
66         try {
67
68             List<String> csids = new ArrayList<String>();
69             if (requestIsForInvocationModeSingle()) {
70                 String singleCsid = getInvocationContext().getSingleCSID();
71                 if (Tools.isBlank(singleCsid)) {
72                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT);
73                 } else {
74                     csids.add(singleCsid);
75                 }
76             } else if (requestIsForInvocationModeList()) {
77                 List<String> listCsids = getListCsids();
78                 if (listCsids.isEmpty()) {
79                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT);
80                 }
81                 csids.addAll(listCsids);
82             } else if (requestIsForInvocationModeGroup()) {
83                 // Currently not supported
84                 // FIXME: Get individual CSIDs, if any, from the group
85                 // String groupCsid = getInvocationContext().getGroupCSID();
86                 // ...
87             } else if (requestIsForInvocationModeNoContext()) {
88                 // Currently not supported
89                 // FIXME: Add code to invoke batch job on every active CollectionObject
90             }
91
92             // Update the computed current location field for each CollectionObject
93             setResults(updateComputedCurrentLocations(csids));
94             setCompletionStatus(STATUS_COMPLETE);
95
96         } catch (Exception e) {
97             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage();
98             setErrorResult(errMsg);
99         }
100
101     }
102
103     // #################################################################
104     // Ray's convenience methods from his AbstractBatchJob class for the
105     // UC Berkeley Botanical Garden v2.4 implementation.
106     // #################################################################
107     protected PoxPayloadOut findByCsid(String serviceName, String csid) throws URISyntaxException, DocumentException {
108         ResourceBase resource = getResourceMap().get(serviceName);
109         return findByCsid(resource, csid);
110     }
111
112     protected PoxPayloadOut findByCsid(ResourceBase resource, String csid) throws URISyntaxException, DocumentException {
113         byte[] response = resource.get(null, createUriInfo(), csid);
114         PoxPayloadOut payload = new PoxPayloadOut(response);
115         return payload;
116     }
117
118     protected UriInfo createUriInfo() throws URISyntaxException {
119         return createUriInfo("");
120     }
121
122     protected UriInfo createUriInfo(String queryString) throws URISyntaxException {
123         URI absolutePath = new URI("");
124         URI baseUri = new URI("");
125         return new UriInfoImpl(absolutePath, baseUri, "", queryString, Collections.<PathSegment>emptyList());
126     }
127
128     // #################################################################
129     // Other convenience methods
130     // #################################################################
131     
132     protected UriInfo createRelatedRecordsUriInfo(String query) throws URISyntaxException {
133         URI uri = new URI(null, null, null, query, null);
134         return createUriInfo(uri.getRawQuery());
135     }
136
137     protected String getFieldElementValue(PoxPayloadOut payload, String partLabel, Namespace partNamespace, String fieldPath) {
138         String value = null;
139         SAXBuilder builder = new SAXBuilder();
140         try {
141             Document document = builder.build(new StringReader(payload.toXML()));
142             Element root = document.getRootElement();
143             // The part element is always expected to have an explicit namespace.
144             Element part = root.getChild(partLabel, partNamespace);
145             // Try getting the field element both with and without a namespace.
146             // Even though a field element that lacks a namespace prefix
147             // may yet inherit its namespace from a parent, JDOM may require that
148             // the getChild() call be made without a namespace.
149             Element field = part.getChild(fieldPath, partNamespace);
150             if (field == null) {
151                 field = part.getChild(fieldPath);
152             }
153             if (field != null) {
154                 value = field.getText();
155             }
156         } catch (Exception e) {
157             logger.error("Error getting value from field path " + fieldPath
158                     + " in schema part " + partLabel);
159             return null;
160         }
161         return value;
162     }
163
164     private InvocationResults updateComputedCurrentLocations(List<String> csids) {
165
166         ResourceMap resourcemap = getResourceMap();
167         ResourceBase collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
168         ResourceBase movementResource = resourcemap.get(MovementClient.SERVICE_NAME);
169         PoxPayloadOut collectionObjectPayload;
170         String computedCurrentLocation;
171         String objectNumber;
172         String movementCsid;
173         int numAffected = 0;
174
175         try {
176
177             // For each CollectionObject record
178             for (String collectionObjectCsid : csids) {
179
180                 if (recordIsDeleted(collectionObjectCsid)) {
181                     continue;
182                 }
183
184                 // Get the movement records related to this record
185
186                 // Get movement records related to this record where the CollectionObject
187                 // record is the subject of the relation
188                 // FIXME: Get query string(s) from constant(s)
189                 String queryString = "rtObj=" + collectionObjectCsid
190                         + "&" + WorkflowClient.WORKFLOW_QUERY_NONDELETED + "=false";
191                 UriInfo uriInfo = createRelatedRecordsUriInfo(queryString);
192
193                 AbstractCommonList relatedMovements = movementResource.getList(uriInfo);
194                 if (logger.isTraceEnabled()) {
195                     logger.trace("Identified " + relatedMovements.getTotalItems()
196                             + " Movement records related to the object CollectionObject record " + collectionObjectCsid);
197                 }
198
199                 // Get movement records related to this record where the CollectionObject
200                 // record is the object of the relation
201                 // FIXME: Get query string(s) from constant(s)
202                 queryString = "rtSbj=" + collectionObjectCsid
203                         + "&" + WorkflowClient.WORKFLOW_QUERY_NONDELETED + "=false";
204                 uriInfo = createRelatedRecordsUriInfo(queryString);
205
206                 AbstractCommonList reverseRelatedMovements = movementResource.getList(uriInfo);
207                 if (logger.isTraceEnabled()) {
208                     logger.trace("Identified " + reverseRelatedMovements.getTotalItems()
209                             + " Movement records related to the subject CollectionObject record " + collectionObjectCsid);
210                 }
211
212                 if ((relatedMovements.getTotalItems() == 0) && reverseRelatedMovements.getTotalItems() == 0) {
213                     continue;
214                 }
215
216                 // Merge the two lists of related movement records
217                 relatedMovements.getListItem().addAll(reverseRelatedMovements.getListItem());
218
219                 // Get the latest movement record from among those, and extract
220                 // its current location value
221                 Set<String> alreadyProcessedMovementCsids = new HashSet<String>();
222                 computedCurrentLocation = "";
223                 String currentLocation;
224                 String locationDate;
225                 String mostRecentLocationDate = "";
226                 for (AbstractCommonList.ListItem movementRecord : relatedMovements.getListItem()) {
227                     movementCsid = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, CSID_ELEMENT_NAME);
228                     if (Tools.isBlank(movementCsid)) {
229                         continue;
230                     }
231                     // Avoid processing any related Movement record more than once,
232                     // regardless of the directionality of its relation(s) to this
233                     // CollectionObject record.
234                     if (alreadyProcessedMovementCsids.contains(movementCsid)) {
235                         continue;
236                     } else {
237                         alreadyProcessedMovementCsids.add(movementCsid);
238                     }
239                     locationDate = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, LOCATION_DATE_ELEMENT_NAME);
240                     if (Tools.isBlank(locationDate)) {
241                         continue;
242                     }
243                     currentLocation = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, CURRENT_LOCATION_ELEMENT_NAME);
244                     if (Tools.isBlank(currentLocation)) {
245                         continue;
246                     }
247                     if (logger.isTraceEnabled()) {
248                         logger.trace("Location date value = " + locationDate);
249                         logger.trace("Current location value = " + currentLocation);
250                     }
251                     // Assumes that all values for this element/field will be consistent ISO 8601
252                     // date/time representations, each of which can be ordered via string comparison.
253                     //
254                     // If this is *not* the case, we can instead parse and convert these values
255                     // to date/time objects.
256                     if (locationDate.compareTo(mostRecentLocationDate) > 0) {
257                         mostRecentLocationDate = locationDate;
258                         // FIXME: Add optional validation here that the currentLocation value
259                         // parses successfully as an item refName
260                         computedCurrentLocation = currentLocation;
261                     }
262
263                 }
264
265                 // Update the computed current location value in the CollectionObject record
266                 collectionObjectPayload = findByCsid(collectionObjectResource, collectionObjectCsid);
267                 if (Tools.notBlank(collectionObjectPayload.toXML())) {
268                     if (logger.isTraceEnabled()) {
269                         logger.trace("Payload: " + "\n" + collectionObjectPayload);
270                     }
271                     objectNumber = getFieldElementValue(collectionObjectPayload,
272                             COLLECTIONOBJECTS_COMMON_SCHEMA_NAME, COLLECTIONOBJECTS_COMMON_NAMESPACE,
273                             OBJECT_NUMBER_ELEMENT_NAME);
274                     if (logger.isTraceEnabled()) {
275                         logger.trace("Object number: " + objectNumber);
276                     }
277                     if (Tools.notBlank(objectNumber)) {
278                         String collectionObjectUpdatePayload =
279                                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
280                                 + "<document name=\"collectionobject\">"
281                                 + "  <ns2:collectionobjects_common "
282                                 + "      xmlns:ns2=\"http://collectionspace.org/services/collectionobject\">"
283                                 + "    <objectNumber>" + objectNumber + "</objectNumber>"
284                                 + "    <computedCurrentLocation>" + computedCurrentLocation + "</computedCurrentLocation>"
285                                 + "  </ns2:collectionobjects_common>"
286                                 + "</document>";
287                         if (logger.isTraceEnabled()) {
288                             logger.trace("Update payload: " + "\n" + collectionObjectUpdatePayload);
289                         }
290                         byte[] response = collectionObjectResource.update(resourcemap, null, collectionObjectCsid,
291                                 collectionObjectUpdatePayload);
292                         numAffected++;
293                         if (logger.isTraceEnabled()) {
294                             logger.trace("Computed current location value for CollectionObject " + collectionObjectCsid
295                                     + " was set to " + computedCurrentLocation);
296                         }
297                     }
298
299                 }
300             }
301         } catch (Exception e) {
302             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage() + " ";
303             errMsg = errMsg + "Successfully updated " + numAffected + " CollectionObject record(s) prior to error.";
304             logger.error(errMsg);
305             setErrorResult(errMsg);
306             getResults().setNumAffected(numAffected);
307             return getResults();
308         }
309
310         logger.info("Updated computedCurrentLocation values in " + numAffected + " CollectionObject records.");
311         getResults().setNumAffected(numAffected);
312         return getResults();
313     }
314
315     private boolean recordIsDeleted(String collectionObjectCsid) {
316         return false;
317         // throw new UnsupportedOperationException("Not yet implemented");
318     }
319 }