]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
b1fb0ad5ce1707fbfd4cb7c306645d1ab943b6fa
[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.dom4j.DocumentException;
30 import org.dom4j.DocumentHelper;
31 import org.dom4j.Element;
32 import org.dom4j.Node;
33 import org.dom4j.XPath;
34 import org.jboss.resteasy.specimpl.UriInfoImpl;
35 import org.nuxeo.ecm.core.api.DocumentModel;
36 import org.nuxeo.ecm.core.api.DocumentModelList;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 public class UpdateObjectLocationBatchJob extends AbstractBatchInvocable {
41
42     // FIXME: Where appropriate, get from existing constants rather than local declarations
43     private final static String COLLECTIONOBJECTS_COMMON_SCHEMA_NAME = "collectionobjects_common";
44     private final static String COLLECTIONOBJECTS_COMMON_SCHEMA = "collectionobjects_common";
45     private final static String MOVEMENTS_COMMON_SCHEMA = MovementConstants.NUXEO_SCHEMA_NAME;
46     private final static String COMPUTED_CURRENT_LOCATION_ELEMENT_NAME = "computedCurrentLocation";
47     private final static String LOCATION_DATE_ELEMENT_NAME = "locationDate";
48     private final static String OBJECT_NUMBER_ELEMENT_NAME = "objectNumber";
49     private InvocationResults results = new InvocationResults();
50     private final String CLASSNAME = this.getClass().getSimpleName();
51     private final Logger logger = LoggerFactory.getLogger(UpdateObjectLocationBatchJob.class);
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             // FIXME: Placeholder during early development
68             if (logger.isInfoEnabled()) {
69                 logger.info("Invoking " + CLASSNAME + " ...");
70                 logger.info("Invocation context is: " + getInvocationContext().getMode());
71             }
72
73             if (!requestedInvocationModeIsSupported()) {
74                 setInvocationModeNotSupportedResult();
75             }
76
77             List<String> csids = new ArrayList<String>();
78             if (requestIsForInvocationModeSingle()) {
79                 String singleCsid = getInvocationContext().getSingleCSID();
80                 if (Tools.notBlank(singleCsid)) {
81                     csids.add(singleCsid);
82                 }
83             } else if (requestIsForInvocationModeList()) {
84                 List<String> listCsids = (getInvocationContext().getListCSIDs().getCsid());
85                 if (listCsids == null || listCsids.isEmpty()) {
86                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT_MESSAGE);
87                 }
88                 csids.addAll(listCsids);
89             } else if (requestIsForInvocationModeGroup()) {
90                 String groupCsid = getInvocationContext().getGroupCSID();
91                 // FIXME: Get individual CSIDs from the group
92                 // and add them to the list
93             }
94
95             if (csids.isEmpty()) {
96                 throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT_MESSAGE);
97             }
98
99             // Update the current computed location field for each CollectionObject
100             setResults(updateComputedCurrentLocations(csids));
101             setCompletionStatus(STATUS_COMPLETE);
102
103         } catch (Exception e) {
104             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage();
105             setErrorResult(errMsg);
106         }
107
108     }
109
110     // Ray's convenience methods from his AbstractBatchJob class for the UC Berkeley Botanical Garden v2.4 implementation.
111     protected PoxPayloadOut findByCsid(String serviceName, String csid) throws URISyntaxException, DocumentException {
112         ResourceBase resource = getResourceMap().get(serviceName);
113         return findByCsid(resource, csid);
114     }
115
116     protected PoxPayloadOut findByCsid(ResourceBase resource, String csid) throws URISyntaxException, DocumentException {
117         byte[] response = resource.get(null, createUriInfo(), csid);
118         PoxPayloadOut payload = new PoxPayloadOut(response);
119         return payload;
120     }
121
122     protected UriInfo createUriInfo() throws URISyntaxException {
123         return createUriInfo("");
124     }
125
126     protected UriInfo createUriInfo(String queryString) throws URISyntaxException {
127         URI absolutePath = new URI("");
128         URI baseUri = new URI("");
129         return new UriInfoImpl(absolutePath, baseUri, "", queryString, Collections.<PathSegment>emptyList());
130     }
131
132     protected UriInfo createRelationSearchUriInfo(String subjectCsid, String objType) throws URISyntaxException {
133         String queryString = "sbj=" + subjectCsid + "&objType=" + objType;
134         URI uri = new URI(null, null, null, queryString, null);
135         return createUriInfo(uri.getRawQuery());
136     }
137
138     /**
139      * Get a field value from a PoxPayloadOut, given a part name and
140      * namespace-qualified xpath expression.
141      */
142     protected String getFieldValue(PoxPayloadOut payload, String partLabel, String namespacePrefix, String namespace, String fieldPath) {
143         String value = null;
144         PayloadOutputPart part = payload.getPart(partLabel);
145
146         if (part != null) {
147             Element element = part.asElement();
148             logger.info(partLabel + " part element =" + element.asXML());
149
150             Map<String, String> namespaceUris = new HashMap<String, String>();
151             namespaceUris.put(namespacePrefix, namespace);
152
153             XPath xPath = DocumentHelper.createXPath(fieldPath);
154             xPath.setNamespaceURIs(namespaceUris);
155
156             Node node = xPath.selectSingleNode(element);
157             // Node node = element.selectSingleNode(fieldPath);
158
159             if (node != null) {
160                 value = node.getText();
161             }
162         }
163
164         return value;
165     }
166
167     protected List<String> getFieldValues(PoxPayloadOut payload, String partLabel, String fieldPath) {
168         List<String> values = new ArrayList<String>();
169         PayloadOutputPart part = payload.getPart(partLabel);
170
171         if (part != null) {
172             Element element = part.asElement();
173             List<Node> nodes = element.selectNodes(fieldPath);
174
175             if (nodes != null) {
176                 for (Node node : nodes) {
177                     values.add(node.getText());
178                 }
179             }
180         }
181
182         return values;
183     }
184
185     private InvocationResults updateComputedCurrentLocations(List<String> csids) {
186
187         ResourceMap resourcemap = getResourceMap();
188         ResourceBase collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
189         ResourceBase movementResource = resourcemap.get(MovementClient.SERVICE_NAME);
190         PoxPayloadOut collectionObjectPayload;
191         String objectNumber;
192         String computedCurrentLocation;
193         int numAffected = 0;
194         // FIXME: Temporary during testing/development
195         final String COMPUTED_CURRENT_LOCATION = "FOO_COMPUTED_CURRENT_LOCATION";
196
197         try {
198
199             // For each CollectionObject record:
200             for (String csid : csids) {
201
202                 // Get the movement records related to this record
203
204                 // FIXME: Create a convenience method for constructing queries like the following
205                 String queryString = "rtObj=" + csid;
206                 URI uri = new URI(null, null, null, queryString, null);
207                 UriInfo uriInfo = createUriInfo(uri.getRawQuery());
208
209                 AbstractCommonList relatedMovements = movementResource.getList(uriInfo);
210                 if (logger.isInfoEnabled()) {
211                     logger.info("Identified " + relatedMovements.getTotalItems()
212                             + " movement records related to CollectionObject record " + csid);
213                 }
214                 if (relatedMovements.getTotalItems() == 0) {
215                     // continue;
216                 }
217
218                 // FIXME: Get the reciprocal relation records, via rtSbj=, as well,
219                 // and remove duplicates
220
221                 // FIXME Temporary for testing, until we integrate the two list results
222                 queryString = "rtSbj=" + csid;
223                 uri = new URI(null, null, null, queryString, null);
224                 uriInfo = createUriInfo(uri.getRawQuery());
225
226                 relatedMovements = movementResource.getList(uriInfo);
227                 if (logger.isInfoEnabled()) {
228                     logger.info("Identified " + relatedMovements.getTotalItems()
229                             + " movement records related to CollectionObject record " + csid);
230                 }
231                 if (relatedMovements.getTotalItems() == 0) {
232                     continue;
233                 }
234
235                 // Get the latest movement record from among those, and extract
236                 // its current location value
237                 computedCurrentLocation = "";
238                 String currentLocation;
239                 String locationDate;
240                 String mostRecentLocationDate = "";
241                 for (AbstractCommonList.ListItem movementRecord : relatedMovements.getListItem()) {
242                     locationDate = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, LOCATION_DATE_ELEMENT_NAME);
243                     if (Tools.notBlank(locationDate)) {
244                         if (logger.isInfoEnabled()) {
245                             logger.info("Location date value = " + locationDate);
246                         }
247                     }
248                     currentLocation = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, COMPUTED_CURRENT_LOCATION_ELEMENT_NAME);
249                     if (Tools.notBlank(currentLocation)) {
250                         if (logger.isInfoEnabled()) {
251                             logger.info("Current location value = " + currentLocation);
252                         }
253                     }
254                     if (Tools.notBlank(locationDate) && Tools.notBlank(currentLocation)) {
255                         // Assumes for  that all values for this element/field will be
256                         // ISO 8601 date values that can be ordered via string comparison.
257                         // We might consider whether to first convert to date values instead.
258                         if (locationDate.compareTo(mostRecentLocationDate) > 1) {
259                             mostRecentLocationDate = locationDate;
260                             computedCurrentLocation = currentLocation;
261                         }
262                     }
263
264                 }
265
266                 // Update the computed current location value in the CollectionObject record
267                 collectionObjectPayload = findByCsid(collectionObjectResource, csid);
268                 if (Tools.notBlank(collectionObjectPayload.toXML())) {
269                     if (logger.isInfoEnabled()) {
270                         logger.info("Payload: " + "\n" + collectionObjectPayload);
271                     }
272                     // Silently fails at various places in dom4j calls (selectSingleNode, selectNode,
273                     // createXpath) in any of the methods tried above, without throwing an Exception
274                     /*
275                      objectNumber = getFieldValue(collectionObjectPayload,
276                      COLLECTIONOBJECTS_COMMON_SCHEMA_NAME,
277                      "ns2", "http://collectionspace.org/services/collectionobject",
278                      OBJECT_NUMBER_ELEMENT_NAME);
279                      if (logger.isInfoEnabled()) {
280                      logger.info("Object number: " + objectNumber);
281                      }
282                      */
283                     objectNumber = "BAR"; // FIXME
284                     if (Tools.notBlank(objectNumber)) {
285                         String collectionObjectUpdatePayload =
286                                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
287                                 + "<document name=\"collectionobject\">"
288                                 + "<ns2:collectionobjects_common xmlns:ns2=\"http://collectionspace.org/services/collectionobject\">"
289                                 + "<objectNumber>" + objectNumber + "</objectNumber>"
290                                 + "<computedCurrentLocation>" + computedCurrentLocation + "</computedCurrentLocation>"
291                                 + "</ns2:collectionobjects_common></document>";
292                         if (logger.isInfoEnabled()) {
293                             logger.info("Update payload: " + "\n" + collectionObjectUpdatePayload);
294                         }
295                         byte[] response = collectionObjectResource.update(resourcemap, null, csid, collectionObjectUpdatePayload);
296                         numAffected++;
297                         if (logger.isTraceEnabled()) {
298                             logger.trace("Computed current location value for CollectionObject " + csid + " set to " + computedCurrentLocation);
299
300                         }
301                     }
302
303                 }
304             }
305         } catch (Exception e) {
306             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage() + " ";
307             errMsg = errMsg + "Successfully updated " + numAffected + " CollectionObject record(s) prior to error.";
308             setErrorResult(errMsg);
309             getResults().setNumAffected(numAffected);
310             return getResults();
311         }
312
313         getResults().setNumAffected(numAffected);
314         return getResults();
315     }
316 }