]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
def2a3d0bc15538d3d125bed4f6ef5ea276324a0
[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.CollectionObjectClient;
15 import org.collectionspace.services.client.MovementClient;
16 import org.collectionspace.services.client.PayloadOutputPart;
17 import org.collectionspace.services.client.PoxPayloadOut;
18 import org.collectionspace.services.client.RelationClient;
19 import org.collectionspace.services.common.ResourceBase;
20 import org.collectionspace.services.common.ResourceMap;
21 import org.collectionspace.services.common.api.Tools;
22 import org.collectionspace.services.common.invocable.InvocationResults;
23 import org.collectionspace.services.jaxb.AbstractCommonList;
24 import org.collectionspace.services.movement.nuxeo.MovementConstants;
25 import org.collectionspace.services.nuxeo.client.java.RepositoryJavaClientImpl;
26 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
27
28 import org.dom4j.DocumentException;
29 import org.dom4j.DocumentHelper;
30 import org.dom4j.Element;
31 import org.dom4j.Node;
32 import org.dom4j.XPath;
33 import org.jboss.resteasy.specimpl.UriInfoImpl;
34 import org.nuxeo.ecm.core.api.DocumentModel;
35 import org.nuxeo.ecm.core.api.DocumentModelList;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 public class UpdateObjectLocationBatchJob extends AbstractBatchInvocable {
40
41     // FIXME; Get from existing constants and replace these local declarations
42     final static String COLLECTIONOBJECTS_COMMON_SCHEMA_NAME = "collectionobjects_common";
43     final static String OBJECT_NUMBER_FIELD_NAME = "objectNumber";
44     private final static String RELATIONS_COMMON_SCHEMA = "relations_common"; // FIXME: Get from external constant
45     private final static String RELATION_DOCTYPE = "Relation"; // FIXME: Get from external constant
46     private final static String SUBJECT_CSID_PROPERTY = "subjectCsid"; // FIXME: Get from external constant
47     private final static String OBJECT_CSID_PROPERTY = "objectCsid"; // FIXME: Get from external constant
48     private final static String SUBJECT_DOCTYPE_PROPERTY = "subjectDocumentType"; // FIXME: Get from external constant
49     private final static String OBJECT_DOCTYPE_PROPERTY = "objectDocumentType"; // FIXME: Get from external constant
50     protected final static String COLLECTIONOBJECTS_COMMON_SCHEMA = "collectionobjects_common"; // FIXME: Get from external constant
51     private final static String COLLECTIONOBJECT_DOCTYPE = "CollectionObject"; // FIXME: Get from external constant
52     protected final static String COMPUTED_CURRENT_LOCATION_PROPERTY = "computedCurrentLocation"; // FIXME: Create and then get from external constant
53     protected final static String MOVEMENTS_COMMON_SCHEMA = "movements_common"; // FIXME: Get from external constant
54     private final static String MOVEMENT_DOCTYPE = MovementConstants.NUXEO_DOCTYPE;
55     private InvocationResults results = new InvocationResults();
56     final String CLASSNAME = this.getClass().getSimpleName();
57     final Logger logger = LoggerFactory.getLogger(UpdateObjectLocationBatchJob.class);
58
59     // Initialization tasks
60     public UpdateObjectLocationBatchJob() {
61         setSupportedInvocationModes(Arrays.asList(INVOCATION_MODE_SINGLE, INVOCATION_MODE_LIST));
62     }
63
64     /**
65      * The main work logic of the batch job. Will be called after setContext.
66      */
67     @Override
68     public void run() {
69
70         setCompletionStatus(STATUS_MIN_PROGRESS);
71
72         try {
73             // FIXME: Placeholder during early development
74             if (logger.isInfoEnabled()) {
75                 logger.info("Invoking " + CLASSNAME + " ...");
76                 logger.info("Invocation context is: " + getInvocationContext().getMode());
77             }
78
79             if (!requestedInvocationModeIsSupported()) {
80                 setInvocationModeNotSupportedResult();
81             }
82
83             List<String> csids = new ArrayList<String>();
84             if (requestIsForInvocationModeSingle()) {
85                 String singleCsid = getInvocationContext().getSingleCSID();
86                 if (Tools.notBlank(singleCsid)) {
87                     csids.add(singleCsid);
88                 }
89             } else if (requestIsForInvocationModeList()) {
90                 List<String> listCsids = (getInvocationContext().getListCSIDs().getCsid());
91                 if (listCsids == null || listCsids.isEmpty()) {
92                     throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT_MESSAGE);
93                 }
94                 csids.addAll(listCsids);
95             } else if (requestIsForInvocationModeGroup()) {
96                 String groupCsid = getInvocationContext().getGroupCSID();
97                 // FIXME: Get individual CSIDs from the group
98                 // and add them to the list
99             }
100
101             if (csids.isEmpty()) {
102                 throw new Exception(CSID_VALUES_NOT_PROVIDED_IN_INVOCATION_CONTEXT_MESSAGE);
103             }
104
105             // Update the current computed location field for each CollectionObject
106             setResults(updateComputedCurrentLocations(csids));
107             setCompletionStatus(STATUS_COMPLETE);
108
109         } catch (Exception e) {
110             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage();
111             setErrorResult(errMsg);
112         }
113
114     }
115
116     // Ray's convenience methods from his AbstractBatchJob class for the UC Berkeley Botanical Garden v2.4 implementation.
117     protected PoxPayloadOut findByCsid(String serviceName, String csid) throws URISyntaxException, DocumentException {
118         ResourceBase resource = getResourceMap().get(serviceName);
119         return findByCsid(resource, csid);
120     }
121
122     protected PoxPayloadOut findByCsid(ResourceBase resource, String csid) throws URISyntaxException, DocumentException {
123         byte[] response = resource.get(null, createUriInfo(), csid);
124         PoxPayloadOut payload = new PoxPayloadOut(response);
125         return payload;
126     }
127
128     protected UriInfo createUriInfo() throws URISyntaxException {
129         return createUriInfo("");
130     }
131
132     protected UriInfo createUriInfo(String queryString) throws URISyntaxException {
133         URI absolutePath = new URI("");
134         URI baseUri = new URI("");
135         return new UriInfoImpl(absolutePath, baseUri, "", queryString, Collections.<PathSegment>emptyList());
136     }
137
138     protected UriInfo createRelationSearchUriInfo(String subjectCsid, String objType) throws URISyntaxException {
139         String queryString = "sbj=" + subjectCsid + "&objType=" + objType;
140         URI uri = new URI(null, null, null, queryString, null);
141         return createUriInfo(uri.getRawQuery());
142     }
143
144     /**
145      * Get a field value from a PoxPayloadOut, given a part name and
146      * namespace-qualified xpath expression.
147      */
148     protected String getFieldValue(PoxPayloadOut payload, String partLabel, String namespacePrefix, String namespace, String fieldPath) {
149         String value = null;
150         PayloadOutputPart part = payload.getPart(partLabel);
151
152         if (part != null) {
153             Element element = part.asElement();
154             logger.info(partLabel + " part element =" + element.asXML());
155
156             Map<String, String> namespaceUris = new HashMap<String, String>();
157             namespaceUris.put(namespacePrefix, namespace);
158
159             XPath xPath = DocumentHelper.createXPath(fieldPath);
160             xPath.setNamespaceURIs(namespaceUris);
161
162             Node node = xPath.selectSingleNode(element);
163             // Node node = element.selectSingleNode(fieldPath);
164
165             if (node != null) {
166                 value = node.getText();
167             }
168         }
169
170         return value;
171     }
172
173     protected List<String> getFieldValues(PoxPayloadOut payload, String partLabel, String fieldPath) {
174         List<String> values = new ArrayList<String>();
175         PayloadOutputPart part = payload.getPart(partLabel);
176
177         if (part != null) {
178             Element element = part.asElement();
179             List<Node> nodes = element.selectNodes(fieldPath);
180
181             if (nodes != null) {
182                 for (Node node : nodes) {
183                     values.add(node.getText());
184                 }
185             }
186         }
187
188         return values;
189     }
190
191     private InvocationResults updateComputedCurrentLocations(List<String> csids) {
192
193         ResourceMap resourcemap = getResourceMap();
194         ResourceBase collectionObjectResource = resourcemap.get(CollectionObjectClient.SERVICE_NAME);
195         ResourceBase movementResource = resourcemap.get(MovementClient.SERVICE_NAME);
196         PoxPayloadOut collectionObjectPayload;
197         String objectNumber;
198         String computedCurrentLocation;
199         int numAffected = 0;
200         // FIXME: Temporary during testing/development
201         final String COMPUTED_CURRENT_LOCATION = "FOO_COMPUTED_CURRENT_LOCATION";
202
203         try {
204
205             // For each CollectionObject record:
206             for (String csid : csids) {
207
208                 // Get the movement records related to this record
209
210                 // FIXME: Create a convenience method for constructing queries like the following
211                 String queryString = "rtObj=" + csid;
212                 URI uri = new URI(null, null, null, queryString, null);
213                 UriInfo uriInfo = createUriInfo(uri.getRawQuery());
214
215                 AbstractCommonList relatedMovements = movementResource.getList(uriInfo);
216                 if (logger.isInfoEnabled()) {
217                     logger.info("Identified " + relatedMovements.getTotalItems()
218                             + " movement records related to CollectionObject record " + csid);
219                 }
220                 if (relatedMovements.getTotalItems() == 0) {
221                     // continue;
222                 }
223                 
224                 /*
225                  * Query resulting from the above:
226                  * Executing CMIS query: SELECT DOC.nuxeo:pathSegment, DOC.dc:title,
227                  * REL.dc:title, REL.relations_common:objectCsid, REL.relations_common:subjectCsid
228                  * FROM Movement DOC JOIN Relation REL ON REL.relations_common:subjectCsid = DOC.nuxeo:pathSegment
229                  * WHERE REL.relations_common:objectCsid = 'c0bdd018-01c1-412a-bc21' AND
230                  * DOC.nuxeo:isVersion = false ORDER BY DOC.collectionspace_core:updatedAt
231                  */
232
233                 // FIXME: Get the reciprocal relation records, via rtSbj=, as well,
234                 // and remove duplicates
235
236                 // FIXME Temporary for testing
237                 queryString = "rtSbj=" + csid;
238                 uri = new URI(null, null, null, queryString, null);
239                 uriInfo = createUriInfo(uri.getRawQuery());
240
241                 relatedMovements = movementResource.getList(uriInfo);
242                 if (logger.isInfoEnabled()) {
243                     logger.info("Identified " + relatedMovements.getTotalItems()
244                             + " movement records related to CollectionObject record " + csid);
245                 }
246                 if (relatedMovements.getTotalItems() == 0) {
247                     continue;
248                 }
249                 
250                 /*
251                  * Query resulting from the above:
252                  * Executing CMIS query: SELECT DOC.nuxeo:pathSegment, DOC.dc:title,
253                  * REL.dc:title, REL.relations_common:objectCsid, REL.relations_common:subjectCsid
254                  * FROM Movement DOC JOIN Relation REL ON REL.relations_common:objectCsid = DOC.nuxeo:pathSegment
255                  * WHERE REL.relations_common:subjectCsid = '7db3c206-3a3c-4f5c-8155' AND
256                  * DOC.nuxeo:isVersion = false ORDER BY DOC.collectionspace_core:updatedAt DESC
257                  */
258
259
260                 /*
261                  // FIXME: Similar to RelationsUtils.buildWhereClause()
262                  // We might consider adding a 'bidirectional where clause' like the following there.
263
264                  String query = String.format(
265                  "SELECT * FROM %1$s WHERE " // collectionspace_core:tenantId =  "
266                  + "("
267                  + "  (%2$s:subjectCsid = '%3$s' "
268                  + "  AND %2$s:objectDocumentType = '%4$s') "
269                  + " OR "
270                  + "  (%2$s:objectCsid = '%3$s' "
271                  + "  AND %2$s:subjectDocumentType = '%4$s') "
272                  + ")"
273                  + ACTIVE_DOCUMENT_WHERE_CLAUSE_FRAGMENT,
274                  RELATION_DOCTYPE, RELATIONS_COMMON_SCHEMA, csid, MOVEMENT_DOCTYPE);
275
276                  relationResource.getList(uriInfo);
277                  query = NuxeoUtils.buildNXQLQuery(csids, null);
278                  DocumentModelList relationDocModels = coreSession.query(query);
279                  */
280
281                 // Get the latest movement record from among those
282
283                 // Extract its current location value
284
285                 // FIXME: Temporary during testing/development
286                 computedCurrentLocation = COMPUTED_CURRENT_LOCATION;
287
288                 // Update the computed current location value in the CollectionObject record
289                 collectionObjectPayload = findByCsid(collectionObjectResource, csid);
290                 if (Tools.notBlank(collectionObjectPayload.toXML())) {
291                     if (logger.isInfoEnabled()) {
292                         logger.info("Payload: " + "\n" + collectionObjectPayload);
293                     }
294                     // Silently fails at various places in dom4j calls (selectSingleNode, selectNode,
295                     // createXpath) in any of the methods tried above, without throwing an Exception
296                     /*
297                      objectNumber = getFieldValue(collectionObjectPayload,
298                      COLLECTIONOBJECTS_COMMON_SCHEMA_NAME,
299                      "ns2", "http://collectionspace.org/services/collectionobject",
300                      OBJECT_NUMBER_FIELD_NAME);
301                      if (logger.isInfoEnabled()) {
302                      logger.info("Object number: " + objectNumber);
303                      }
304                      */
305                     objectNumber = "BAR"; // FIXME
306                     if (Tools.notBlank(objectNumber)) {
307                         String collectionObjectUpdatePayload =
308                                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
309                                 + "<document name=\"collectionobject\">"
310                                 + "<ns2:collectionobjects_common xmlns:ns2=\"http://collectionspace.org/services/collectionobject\">"
311                                 + "<objectNumber>" + objectNumber + "</objectNumber>"
312                                 + "<computedCurrentLocation>" + computedCurrentLocation + "</computedCurrentLocation>"
313                                 + "</ns2:collectionobjects_common></document>";
314                         if (logger.isInfoEnabled()) {
315                             logger.info("Update payload: " + "\n" + collectionObjectUpdatePayload);
316                         }
317                         byte[] response = collectionObjectResource.update(resourcemap, null, csid, collectionObjectUpdatePayload);
318                         numAffected++;
319                         if (logger.isTraceEnabled()) {
320                             logger.trace("Computed current location value for CollectionObject " + csid + " set to " + computedCurrentLocation);
321
322                         }
323                     }
324
325                 }
326             }
327         } catch (Exception e) {
328             String errMsg = "Error encountered in " + CLASSNAME + ": " + e.getLocalizedMessage() + " ";
329             errMsg = errMsg + "Successfully updated " + numAffected + " CollectionObject record(s) prior to error.";
330             setErrorResult(errMsg);
331             getResults().setNumAffected(numAffected);
332             return getResults();
333         }
334
335         getResults()
336                 .setNumAffected(numAffected);
337         return getResults();
338     }
339 }