]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
3787d2e3a45e9fb17ddc466c9018773304abbc6a
[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 CURRENT_LOCATION_ELEMENT_NAME = "currentLocation";
48     private final static String LOCATION_DATE_ELEMENT_NAME = "locationDate";
49     private final static String OBJECT_NUMBER_ELEMENT_NAME = "objectNumber";
50     private InvocationResults results = new InvocationResults();
51     private final String CLASSNAME = this.getClass().getSimpleName();
52     private final Logger logger = LoggerFactory.getLogger(UpdateObjectLocationBatchJob.class);
53
54     // Initialization tasks
55     public UpdateObjectLocationBatchJob() {
56         setSupportedInvocationModes(Arrays.asList(INVOCATION_MODE_SINGLE, INVOCATION_MODE_LIST));
57     }
58
59     /**
60      * The main work logic of the batch job. Will be called after setContext.
61      */
62     @Override
63     public void run() {
64
65         setCompletionStatus(STATUS_MIN_PROGRESS);
66
67         try {
68             // FIXME: Placeholder during early development
69             if (logger.isInfoEnabled()) {
70                 logger.info("Invoking " + CLASSNAME + " ...");
71                 logger.info("Invocation context is: " + getInvocationContext().getMode());
72             }
73
74             if (!requestedInvocationModeIsSupported()) {
75                 setInvocationModeNotSupportedResult();
76             }
77
78             List<String> csids = new ArrayList<String>();
79             if (requestIsForInvocationModeSingle()) {
80                 String singleCsid = getInvocationContext().getSingleCSID();
81                 if (Tools.notBlank(singleCsid)) {
82                     csids.add(singleCsid);
83                 }
84             } else if (requestIsForInvocationModeList()) {
85                 List<String> listCsids = (getInvocationContext().getListCSIDs().getCsid());
86                 if (listCsids == null || listCsids.isEmpty()) {
87                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT_MESSAGE);
88                 }
89                 csids.addAll(listCsids);
90             } else if (requestIsForInvocationModeGroup()) {
91                 String groupCsid = getInvocationContext().getGroupCSID();
92                 // FIXME: Get individual CSIDs from the group
93                 // and add them to the list
94             }
95
96             if (csids.isEmpty()) {
97                 throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT_MESSAGE);
98             }
99
100             // Update the current computed location field for each CollectionObject
101             setResults(updateComputedCurrentLocations(csids));
102             setCompletionStatus(STATUS_COMPLETE);
103
104         } catch (Exception e) {
105             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage();
106             setErrorResult(errMsg);
107         }
108
109     }
110
111     // Ray's convenience methods from his AbstractBatchJob class for the UC Berkeley Botanical Garden v2.4 implementation.
112     protected PoxPayloadOut findByCsid(String serviceName, String csid) throws URISyntaxException, DocumentException {
113         ResourceBase resource = getResourceMap().get(serviceName);
114         return findByCsid(resource, csid);
115     }
116
117     protected PoxPayloadOut findByCsid(ResourceBase resource, String csid) throws URISyntaxException, DocumentException {
118         byte[] response = resource.get(null, createUriInfo(), csid);
119         PoxPayloadOut payload = new PoxPayloadOut(response);
120         return payload;
121     }
122
123     protected UriInfo createUriInfo() throws URISyntaxException {
124         return createUriInfo("");
125     }
126
127     protected UriInfo createUriInfo(String queryString) throws URISyntaxException {
128         URI absolutePath = new URI("");
129         URI baseUri = new URI("");
130         return new UriInfoImpl(absolutePath, baseUri, "", queryString, Collections.<PathSegment>emptyList());
131     }
132
133     protected UriInfo createRelationSearchUriInfo(String subjectCsid, String objType) throws URISyntaxException {
134         String queryString = "sbj=" + subjectCsid + "&objType=" + objType;
135         URI uri = new URI(null, null, null, queryString, null);
136         return createUriInfo(uri.getRawQuery());
137     }
138
139     /**
140      * Get a field value from a PoxPayloadOut, given a part name and
141      * namespace-qualified xpath expression.
142      */
143     protected String getFieldValue(PoxPayloadOut payload, String partLabel, String namespacePrefix, String namespace, String fieldPath) {
144         String value = null;
145         PayloadOutputPart part = payload.getPart(partLabel);
146
147         if (part != null) {
148             Element element = part.asElement();
149             logger.info(partLabel + " part element =" + element.asXML());
150
151             Map<String, String> namespaceUris = new HashMap<String, String>();
152             namespaceUris.put(namespacePrefix, namespace);
153
154             XPath xPath = DocumentHelper.createXPath(fieldPath);
155             xPath.setNamespaceURIs(namespaceUris);
156
157             Node node = xPath.selectSingleNode(element);
158             // Node node = element.selectSingleNode(fieldPath);
159
160             if (node != null) {
161                 value = node.getText();
162             }
163         }
164
165         return value;
166     }
167
168     protected List<String> getFieldValues(PoxPayloadOut payload, String partLabel, String fieldPath) {
169         List<String> values = new ArrayList<String>();
170         PayloadOutputPart part = payload.getPart(partLabel);
171
172         if (part != null) {
173             Element element = part.asElement();
174             List<Node> nodes = element.selectNodes(fieldPath);
175
176             if (nodes != null) {
177                 for (Node node : nodes) {
178                     values.add(node.getText());
179                 }
180             }
181         }
182
183         return values;
184     }
185
186     private InvocationResults updateComputedCurrentLocations(List<String> csids) {
187
188         ResourceMap resourcemap = getResourceMap();
189         ResourceBase collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
190         ResourceBase movementResource = resourcemap.get(MovementClient.SERVICE_NAME);
191         PoxPayloadOut collectionObjectPayload;
192         String objectNumber;
193         String computedCurrentLocation;
194         int numAffected = 0;
195         // FIXME: Temporary during testing/development
196         final String COMPUTED_CURRENT_LOCATION = "FOO_COMPUTED_CURRENT_LOCATION";
197
198         try {
199
200             // For each CollectionObject record:
201             for (String csid : csids) {
202
203                 // Get the movement records related to this record
204
205                 // FIXME: Create a convenience method for constructing queries like the following
206                 String queryString = "rtObj=" + csid;
207                 URI uri = new URI(null, null, null, queryString, null);
208                 UriInfo uriInfo = createUriInfo(uri.getRawQuery());
209
210                 AbstractCommonList relatedMovements = movementResource.getList(uriInfo);
211                 if (logger.isInfoEnabled()) {
212                     logger.info("Identified " + relatedMovements.getTotalItems()
213                             + " movement records related to CollectionObject record " + csid);
214                 }
215
216                 // FIXME: Get relation records in the reverse direction as well,
217                 // via rtSbj=, merge with records obtained above, and remove duplicates
218
219                 queryString = "rtSbj=" + csid;
220                 uri = new URI(null, null, null, queryString, null);
221                 uriInfo = createUriInfo(uri.getRawQuery());
222
223                 AbstractCommonList reverseRelatedMovements = movementResource.getList(uriInfo);
224                 if (logger.isInfoEnabled()) {
225                     logger.info("Identified " + reverseRelatedMovements.getTotalItems()
226                             + " movement records related in the reverse  to CollectionObject record " + csid);
227                 }
228                 
229                 if ((relatedMovements.getTotalItems() == 0) && reverseRelatedMovements.getTotalItems() == 0) {
230                     continue;
231                 }
232                 
233                 // Merge the two lists
234                 relatedMovements.getListItem().addAll(reverseRelatedMovements.getListItem());
235
236                 // Get the latest movement record from among those, and extract
237                 // its current location value
238                 computedCurrentLocation = "";
239                 String currentLocation;
240                 String locationDate;
241                 String mostRecentLocationDate = "";
242                 for (AbstractCommonList.ListItem movementRecord : relatedMovements.getListItem()) {
243                     locationDate = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, LOCATION_DATE_ELEMENT_NAME);
244                     if (Tools.notBlank(locationDate)) {
245                         if (logger.isInfoEnabled()) {
246                             logger.info("Location date value = " + locationDate);
247                         }
248                     }
249                     currentLocation = AbstractCommonListUtils.ListItemGetElementValue(movementRecord, CURRENT_LOCATION_ELEMENT_NAME);
250                     if (Tools.notBlank(currentLocation)) {
251                         if (logger.isInfoEnabled()) {
252                             logger.info("Current location value = " + currentLocation);
253                         }
254                     }
255                     if (Tools.notBlank(locationDate) && Tools.notBlank(currentLocation)) {
256                         // Assumes that all values for this element/field will be ISO 8601
257                         // date representations, each of which can be ordered via string comparison.
258                         if (locationDate.compareTo(mostRecentLocationDate) > 0) {
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 }