]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
a61f30f63f6c45938d02563deb9e5d794bb42853
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.batch.nuxeo;
2
3 import java.net.URI;
4 import java.net.URISyntaxException;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.Map;
11 import javax.ws.rs.core.PathSegment;
12 import javax.ws.rs.core.UriInfo;
13 import org.collectionspace.services.batch.AbstractBatchInvocable;
14 import org.collectionspace.services.client.AbstractCommonListUtils;
15 import org.collectionspace.services.client.CollectionObjectClient;
16 import org.collectionspace.services.client.MovementClient;
17 import org.collectionspace.services.client.PayloadOutputPart;
18 import org.collectionspace.services.client.PoxPayloadOut;
19 import org.collectionspace.services.client.RelationClient;
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.InvocationResults;
24 import org.collectionspace.services.jaxb.AbstractCommonList;
25 import org.collectionspace.services.movement.nuxeo.MovementConstants;
26 import org.collectionspace.services.nuxeo.client.java.RepositoryJavaClientImpl;
27 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
28
29 import org.jdom.Document;
30 import org.jdom.Element;
31 import org.jdom.input.SAXBuilder;
32
33 import java.io.StringReader;
34
35 import org.dom4j.DocumentException;
36
37
38 /*
39  import org.dom4j.DocumentHelper;
40  import org.dom4j.Element;
41  import org.dom4j.Node;
42  import org.dom4j.XPath;
43  */
44
45 import org.jboss.resteasy.specimpl.UriInfoImpl;
46 import org.jdom.Namespace;
47 import org.nuxeo.ecm.core.api.DocumentModel;
48 import org.nuxeo.ecm.core.api.DocumentModelList;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 public class UpdateObjectLocationBatchJob extends AbstractBatchInvocable {
53
54     // FIXME: Where appropriate, get from existing constants rather than local declarations
55     private final static String COLLECTIONOBJECTS_COMMON_SCHEMA_NAME = "collectionobjects_common";
56     private final static String COLLECTIONOBJECTS_COMMON_SCHEMA = "collectionobjects_common";
57     private final static String MOVEMENTS_COMMON_SCHEMA = MovementConstants.NUXEO_SCHEMA_NAME;
58     private final static String COMPUTED_CURRENT_LOCATION_ELEMENT_NAME = "computedCurrentLocation";
59     private final static String CURRENT_LOCATION_ELEMENT_NAME = "currentLocation";
60     private final static String LOCATION_DATE_ELEMENT_NAME = "locationDate";
61     private final static String OBJECT_NUMBER_ELEMENT_NAME = "objectNumber";
62     private final static Namespace COLLECTIONOBJECTS_COMMON_NAMESPACE =
63             Namespace.getNamespace("ns2", "http://collectionspace.org/services/collectionobject");
64     private InvocationResults results = new InvocationResults();
65     private final String CLASSNAME = this.getClass().getSimpleName();
66     private final Logger logger = LoggerFactory.getLogger(UpdateObjectLocationBatchJob.class);
67
68     // Initialization tasks
69     public UpdateObjectLocationBatchJob() {
70         setSupportedInvocationModes(Arrays.asList(INVOCATION_MODE_SINGLE, INVOCATION_MODE_LIST));
71     }
72
73     /**
74      * The main work logic of the batch job. Will be called after setContext.
75      */
76     @Override
77     public void run() {
78
79         setCompletionStatus(STATUS_MIN_PROGRESS);
80
81         try {
82             // FIXME: Placeholder during early development
83             if (logger.isInfoEnabled()) {
84                 logger.info("Invoking " + CLASSNAME + " ...");
85                 logger.info("Invocation context is: " + getInvocationContext().getMode());
86             }
87
88             if (!requestedInvocationModeIsSupported()) {
89                 setInvocationModeNotSupportedResult();
90             }
91
92             List<String> csids = new ArrayList<String>();
93             if (requestIsForInvocationModeSingle()) {
94                 String singleCsid = getInvocationContext().getSingleCSID();
95                 if (Tools.notBlank(singleCsid)) {
96                     csids.add(singleCsid);
97                 }
98             } else if (requestIsForInvocationModeList()) {
99                 List<String> listCsids = (getInvocationContext().getListCSIDs().getCsid());
100                 if (listCsids == null || listCsids.isEmpty()) {
101                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT_MESSAGE);
102                 }
103                 csids.addAll(listCsids);
104             } else if (requestIsForInvocationModeGroup()) {
105                 String groupCsid = getInvocationContext().getGroupCSID();
106                 // FIXME: Get individual CSIDs from the group
107                 // and add them to the list
108             }
109
110             if (csids.isEmpty()) {
111                 throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT_MESSAGE);
112             }
113
114             // Update the current computed location field for each CollectionObject
115             setResults(updateComputedCurrentLocations(csids));
116             setCompletionStatus(STATUS_COMPLETE);
117
118         } catch (Exception e) {
119             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage();
120             setErrorResult(errMsg);
121         }
122
123     }
124
125     // Ray's convenience methods from his AbstractBatchJob class for the UC Berkeley Botanical Garden v2.4 implementation.
126     protected PoxPayloadOut findByCsid(String serviceName, String csid) throws URISyntaxException, DocumentException {
127         ResourceBase resource = getResourceMap().get(serviceName);
128         return findByCsid(resource, csid);
129     }
130
131     protected PoxPayloadOut findByCsid(ResourceBase resource, String csid) throws URISyntaxException, DocumentException {
132         byte[] response = resource.get(null, createUriInfo(), csid);
133         PoxPayloadOut payload = new PoxPayloadOut(response);
134         return payload;
135     }
136
137     protected UriInfo createUriInfo() throws URISyntaxException {
138         return createUriInfo("");
139     }
140
141     protected UriInfo createUriInfo(String queryString) throws URISyntaxException {
142         URI absolutePath = new URI("");
143         URI baseUri = new URI("");
144         return new UriInfoImpl(absolutePath, baseUri, "", queryString, Collections.<PathSegment>emptyList());
145     }
146
147     protected UriInfo createRelationSearchUriInfo(String subjectCsid, String objType) throws URISyntaxException {
148         String queryString = "sbj=" + subjectCsid + "&objType=" + objType;
149         URI uri = new URI(null, null, null, queryString, null);
150         return createUriInfo(uri.getRawQuery());
151     }
152
153     protected String getFieldValue(PoxPayloadOut payload, String partLabel, Namespace partNamespace, String fieldPath) {
154         String value = null;
155         SAXBuilder builder = new SAXBuilder();
156         try {
157             Document document = builder.build(new StringReader(payload.toXML()));
158             Element root = document.getRootElement();
159             Element part = root.getChild(partLabel, partNamespace);
160             Element field = part.getChild(fieldPath, partNamespace);
161             value = field.getText();
162         } catch (Exception e) {
163             logger.error("Error getting value from field path " + fieldPath
164                     + " in schema part " + partLabel);
165             return null;
166         }
167         return value;
168     }
169
170     /**
171      * Get a field value from a PoxPayloadOut, given a part name and xpath
172      * expression.
173      */
174     /*
175      protected String getFieldValue(PoxPayloadOut payload, String partLabel, String fieldPath) {
176      String value = null;
177      PayloadOutputPart part = payload.getPart(partLabel);
178
179      if (part != null) {
180      Element element = part.asElement();
181      Node node = element.selectSingleNode(fieldPath);
182
183      if (node != null) {
184      value = node.getText();
185      }
186      }
187
188      return value;
189      }
190      */
191     /**
192      * Get a field value from a PoxPayloadOut, given a part name and
193      * namespace-qualified xpath expression.
194      */
195     /*
196      protected String getFieldValue(PoxPayloadOut payload, String partLabel, String namespacePrefix, String namespace, String fieldPath) {
197      String value = null;
198      PayloadOutputPart part = payload.getPart(partLabel);
199
200      if (part != null) {
201      Element element = part.asElement();
202      logger.info(partLabel + " part element =" + element.asXML());
203
204      Map<String, String> namespaceUris = new HashMap<String, String>();
205      namespaceUris.put(namespacePrefix, namespace);
206
207      XPath xPath = DocumentHelper.createXPath(fieldPath);
208      xPath.setNamespaceURIs(namespaceUris);
209
210      Node node = xPath.selectSingleNode(element);
211      // Node node = element.selectSingleNode(fieldPath);
212
213      if (node != null) {
214      value = node.getText();
215      }
216      }
217
218      return value;
219      }
220      */
221     private InvocationResults updateComputedCurrentLocations(List<String> csids) {
222
223         ResourceMap resourcemap = getResourceMap();
224         ResourceBase collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
225         ResourceBase movementResource = resourcemap.get(MovementClient.SERVICE_NAME);
226         PoxPayloadOut collectionObjectPayload;
227         String objectNumber;
228         String computedCurrentLocation;
229         int numAffected = 0;
230
231         try {
232
233             // For each CollectionObject record:
234             for (String csid : csids) {
235
236                 // Get the movement records related to this record
237
238                 // FIXME: Create a convenience method for constructing queries like the following
239                 String queryString = "rtObj=" + csid; // FIXME: Get from constant
240                 URI uri = new URI(null, null, null, queryString, null);
241                 UriInfo uriInfo = createUriInfo(uri.getRawQuery());
242
243                 AbstractCommonList relatedMovements = movementResource.getList(uriInfo);
244                 if (logger.isInfoEnabled()) {
245                     logger.info("Identified " + relatedMovements.getTotalItems()
246                             + " Movement records related to the object CollectionObject record " + csid);
247                 }
248
249                 // Get relation records in the reverse direction as well,
250                 // merge with records obtained above, and remove duplicates
251                 queryString = "rtSbj=" + csid; // FIXME: Get from constant
252                 uri = new URI(null, null, null, queryString, null);
253                 uriInfo = createUriInfo(uri.getRawQuery());
254
255                 AbstractCommonList reverseRelatedMovements = movementResource.getList(uriInfo);
256                 if (logger.isInfoEnabled()) {
257                     logger.info("Identified " + reverseRelatedMovements.getTotalItems()
258                             + " Movement records related to the subject CollectionObject record " + csid);
259                 }
260
261                 if ((relatedMovements.getTotalItems() == 0) && reverseRelatedMovements.getTotalItems() == 0) {
262                     continue;
263                 }
264
265                 // Merge the two lists
266                 relatedMovements.getListItem().addAll(reverseRelatedMovements.getListItem());
267
268                 // Get the latest movement record from among those, and extract
269                 // its current location value
270                 computedCurrentLocation = "";
271                 String currentLocation;
272                 String locationDate;
273                 String mostRecentLocationDate = "";
274                 for (AbstractCommonList.ListItem movementRecord : relatedMovements.getListItem()) {
275
276                     // FIXME: Add 'de-duping' code here to avoid processing any
277                     // related Movement record more than once
278
279                     locationDate = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, LOCATION_DATE_ELEMENT_NAME);
280                     if (Tools.isBlank(locationDate)) {
281                         continue;
282                     }
283                     currentLocation = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, CURRENT_LOCATION_ELEMENT_NAME);
284                     if (Tools.isBlank(currentLocation)) {
285                         continue;
286                     }
287                     if (logger.isInfoEnabled()) {
288                         logger.info("Location date value = " + locationDate);
289                         logger.info("Current location value = " + currentLocation);
290                     }
291                     // Assumes that all values for this element/field will be consistent ISO 8601
292                     // date/time representations, each of which can be ordered via string comparison.
293                     if (locationDate.compareTo(mostRecentLocationDate) > 0) {
294                         mostRecentLocationDate = locationDate;
295                         // FIXME: Add validation here that the currentLocation value parses successfully as an item refName
296                         computedCurrentLocation = currentLocation;
297                     }
298
299                 }
300
301                 // Update the computed current location value in the CollectionObject record
302                 collectionObjectPayload = findByCsid(collectionObjectResource, csid);
303                 if (Tools.notBlank(collectionObjectPayload.toXML())) {
304                     if (logger.isInfoEnabled()) {
305                         logger.info("Payload: " + "\n" + collectionObjectPayload);
306                     }
307                     // Silently fails at various places in dom4j calls (selectSingleNode, selectNode,
308                     // createXpath) in any of the methods tried, without throwing an Exception.
309                     //
310                     // Those methods are now commented out, in favor of a replacement, however temporary,
311                     // using JDOM.
312                     //
313                     // FIXME: Get namespace from constant; verify whether prefix or URI is required
314                     objectNumber = getFieldValue(collectionObjectPayload,
315                             COLLECTIONOBJECTS_COMMON_SCHEMA_NAME, COLLECTIONOBJECTS_COMMON_NAMESPACE,
316                             OBJECT_NUMBER_ELEMENT_NAME);
317                     if (logger.isInfoEnabled()) {
318                         logger.info("Object number: " + objectNumber);
319                     }
320                     if (Tools.notBlank(objectNumber)) {
321                         String collectionObjectUpdatePayload =
322                                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
323                                 + "<document name=\"collectionobject\">"
324                                 + "<ns2:collectionobjects_common xmlns:ns2=\"http://collectionspace.org/services/collectionobject\">"
325                                 + "<objectNumber>" + objectNumber + "</objectNumber>"
326                                 + "<computedCurrentLocation>" + computedCurrentLocation + "</computedCurrentLocation>"
327                                 + "</ns2:collectionobjects_common></document>";
328                         if (logger.isInfoEnabled()) {
329                             logger.info("Update payload: " + "\n" + collectionObjectUpdatePayload);
330                         }
331                         byte[] response = collectionObjectResource.update(resourcemap, null, csid, collectionObjectUpdatePayload);
332                         numAffected++;
333                         if (logger.isTraceEnabled()) {
334                             logger.trace("Computed current location value for CollectionObject " + csid + " set to " + computedCurrentLocation);
335
336                         }
337                     }
338
339                 }
340             }
341         } catch (Exception e) {
342             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage() + " ";
343             errMsg = errMsg + "Successfully updated " + numAffected + " CollectionObject record(s) prior to error.";
344             logger.error(errMsg);
345             setErrorResult(errMsg);
346             getResults().setNumAffected(numAffected);
347             return getResults();
348         }
349
350         getResults().setNumAffected(numAffected);
351         return getResults();
352     }
353 }