]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
162b982d2d6772149d2c404058220bf21f558bbb
[tmp/jakarta-migration.git] /
1 /**
2  *  This document is a part of the source code and related artifacts
3  *  for CollectionSpace, an open source collections management system
4  *  for museums and related institutions:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
18  *  Unless required by applicable law or agreed to in writing, software
19  *  distributed under the License is distributed on an "AS IS" BASIS,
20  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  *  See the License for the specific language governing permissions and
22  *  limitations under the License.
23  */
24 package org.collectionspace.services.nuxeo.client.java;
25
26 import java.lang.reflect.Method;
27 import java.util.ArrayList;
28 import java.util.GregorianCalendar;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.List;
32
33 import javax.ws.rs.core.MultivaluedMap;
34
35 import org.collectionspace.services.client.CollectionSpaceClient;
36 import org.collectionspace.services.client.IQueryManager;
37 import org.collectionspace.services.client.IRelationsManager;
38 import org.collectionspace.services.client.PoxPayload;
39 import org.collectionspace.services.client.PoxPayloadIn;
40 import org.collectionspace.services.client.PoxPayloadOut;
41 import org.collectionspace.services.client.XmlTools;
42 import org.collectionspace.services.client.workflow.WorkflowClient;
43 import org.collectionspace.services.common.ReflectionMapper;
44 import org.collectionspace.services.common.api.GregorianCalendarDateTimeUtils;
45 import org.collectionspace.services.common.api.Tools;
46 import org.collectionspace.services.common.config.ServiceConfigUtils;
47 import org.collectionspace.services.common.context.AbstractServiceContextImpl;
48 import org.collectionspace.services.common.context.ServiceContext;
49 import org.collectionspace.services.common.document.DocumentException;
50 import org.collectionspace.services.common.document.DocumentWrapper;
51 import org.collectionspace.services.common.query.QueryContext;
52 import org.collectionspace.services.common.relation.nuxeo.RelationsUtils;
53 import org.collectionspace.services.config.service.DocHandlerParams;
54 import org.collectionspace.services.config.service.ListResultField;
55 import org.collectionspace.services.jaxb.AbstractCommonList;
56 import org.collectionspace.services.nuxeo.client.java.CommonList;
57 import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
58 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
59 import org.dom4j.Document;
60 import org.nuxeo.ecm.core.api.DocumentModel;
61 import org.nuxeo.ecm.core.api.DocumentModelList;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 /**
66  * This class is generified by the marker type T,
67  * where T is expected to map to something like BlobCommon, MediaCommon, ObjectexitCommon, etc.,
68  * and so on for every JAXB-generated schema class.
69  *
70  * User: laramie
71  * $LastChangedRevision: $
72  * $LastChangedDate: $
73  *
74  */
75 public abstract class NuxeoDocumentModelHandler<T> extends RemoteDocumentModelHandlerImpl<T, AbstractCommonList> {
76
77         /** The logger. */
78         private final Logger logger = LoggerFactory.getLogger(this.getClass());
79
80         private AbstractCommonList commonList;
81
82         protected static final int NUM_STANDARD_LIST_RESULT_FIELDS = 5;
83         protected static final String STANDARD_LIST_CSID_FIELD = "csid";
84         protected static final String STANDARD_LIST_URI_FIELD = CollectionSpaceClient.COLLECTIONSPACE_CORE_URI;
85         protected static final String STANDARD_LIST_REFNAME_FIELD = CollectionSpaceClient.COLLECTIONSPACE_CORE_REFNAME;
86         protected static final String STANDARD_LIST_UPDATED_AT_FIELD = CollectionSpaceClient.COLLECTIONSPACE_CORE_UPDATED_AT;
87         protected static final String STANDARD_LIST_WORKFLOW_FIELD = CollectionSpaceClient.COLLECTIONSPACE_CORE_WORKFLOWSTATE;
88         protected static final String STANDARD_LIST_MARK_RT_FIELD = "related";
89
90         @Override
91         public AbstractCommonList getCommonPartList() {
92                 return commonList;
93         }
94
95         @Override
96         public void setCommonPartList(AbstractCommonList aCommonList) {
97                 this.commonList = aCommonList;
98         }
99
100         private T commonPart;
101
102         @Override
103         public T getCommonPart() {
104                 return (T) commonPart;
105         }
106
107         @Override
108         public void setCommonPart(T commonPart) {
109                 this.commonPart = commonPart;
110         }
111
112     /**
113      * The entity type expected from the JAX-RS Response object.  By default it is of type String.  Child classes
114      * can override this if they need to.
115      */
116     protected Class<String> getEntityResponseType() {
117         return String.class;
118     }
119     
120     protected String getWorkflowState(PoxPayload payload) {
121         String result = null;
122         
123                 Document document = payload.getDOMDocument();
124                 result = XmlTools.getElementValue(document, "//" + WorkflowClient.WORKFLOWSTATE_XML_ELEMENT_NAME);
125                 
126                 return result;
127     }
128     
129     protected Long getRevision(PoxPayload payload) {
130         Long result = null;
131         
132                 Document document = payload.getDOMDocument();
133                 String xmlRev = XmlTools.getElementValue(document, "//rev");
134                 result = Long.valueOf(xmlRev);
135                 
136                 return result;
137     }
138     
139     protected List getItemList(PoxPayloadIn payloadIn) {
140         List result = null;
141         
142                 Document document = payloadIn.getDOMDocument();
143                 result = XmlTools.getElementNodes(document, "//list-item");
144                 
145                 return result;
146     }
147
148         /**
149          * Subclass DocHandlers may override this method to control exact creation of the common list.
150          * This class instantiates an AbstractCommonList from the classname returned by getDocHandlerParams().AbstractCommonListClassname.
151          * 
152          * @return
153          * @throws Exception
154          */
155         public AbstractCommonList createAbstractCommonListImpl() throws Exception {
156                 // String classname = this.commonList.getClass().getName();
157                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
158                 String classname = ServiceConfigUtils.getDocHandlerParams(ctx).getAbstractCommonListClassname();
159                 if (classname == null) {
160                         throw new Exception(
161                                         "in createAbstractCommonListImpl. getDocHandlerParams().getAbstractCommonListClassname() is null");
162                 }
163                 classname = classname.trim();
164                 return (AbstractCommonList) (ReflectionMapper.instantiate(classname));
165         }
166
167         /** DocHandlerBase calls this method with the CSID as id */
168         public Object createItemForCommonList(DocumentModel docModel, String label, String id) throws Exception {
169                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
170                 return createItemForCommonList(ServiceConfigUtils.getDocHandlerParams(ctx).getCommonListItemClassname(),
171                                 docModel, label, id, true);
172         }
173
174         public String getSummaryFields(AbstractCommonList theCommonList) throws DocumentException {
175                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
176                 return ServiceConfigUtils.getDocHandlerParams(ctx).getSummaryFields();
177         }
178
179         public void setListItemArrayExtended(boolean isExtended) throws DocumentException {
180                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
181                 ServiceConfigUtils.getDocHandlerParams(ctx).getListResultsFields().setExtended(isExtended);
182         }
183
184         public boolean isListItemArrayExtended() throws DocumentException {
185                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
186                 return ServiceConfigUtils.getDocHandlerParams(ctx).getListResultsFields().isExtended();
187         }
188
189         public List<ListResultField> getListItemsArray() throws DocumentException {
190                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
191                 return ServiceConfigUtils.getDocHandlerParams(ctx).getListResultsFields().getListResultField();
192         }
193
194         @Override
195         public T extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
196                 throw new UnsupportedOperationException();
197         }
198
199         @Override
200         public void fillCommonPart(T objectexitObject, DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
201                 throw new UnsupportedOperationException();
202         }
203
204         protected static String getRefname(DocumentModel docModel) throws Exception {
205                 String result = (String) docModel.getProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
206                                 CollectionSpaceClient.COLLECTIONSPACE_CORE_REFNAME);
207                 return result;
208         }
209
210         public static String getUpdatedAtAsString(DocumentModel docModel) throws Exception {
211                 GregorianCalendar cal = (GregorianCalendar) docModel.getProperty(
212                                 CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
213                                 CollectionSpaceClient.COLLECTIONSPACE_CORE_UPDATED_AT);
214                 String updatedAt = GregorianCalendarDateTimeUtils.formatAsISO8601Timestamp(cal);
215                 return updatedAt;
216         }
217
218         @Override
219         public AbstractCommonList extractCommonPartList(DocumentWrapper<DocumentModelList> wrapDoc) throws Exception {
220                 CommonList commonList = new CommonList();
221                 CoreSessionInterface repoSession = null;
222                 NuxeoRepositoryClientImpl repoClient = null;
223                 boolean releaseRepoSession = false;
224
225                 AbstractServiceContextImpl ctx = (AbstractServiceContextImpl) getServiceContext();
226                 MultivaluedMap<String, String> queryParams = getServiceContext().getQueryParams();
227                 String markRtSbj = queryParams.getFirst(IQueryManager.MARK_RELATED_TO_CSID_AS_SUBJECT);
228                 if (Tools.isBlank(markRtSbj)) {
229                         markRtSbj = null;
230                 }
231                 
232                 //
233                 // We may be being asked to mark the record as related independent of whether it is the subject or object of a relationship.
234                 //
235                 String markRtSbjOrObj = queryParams.getFirst(IQueryManager.MARK_RELATED_TO_CSID_AS_EITHER);
236                 if (Tools.isBlank(markRtSbjOrObj)) {
237                         markRtSbjOrObj = null;
238                 } else {
239                         if (Tools.isBlank(markRtSbj) == false) {
240                                 logger.warn(String.format("Ignoring query param %s=%s since overriding query param %s=%s exists.",
241                                                 IQueryManager.MARK_RELATED_TO_CSID_AS_SUBJECT, markRtSbj, IQueryManager.MARK_RELATED_TO_CSID_AS_EITHER, markRtSbjOrObj));
242                         }
243                         markRtSbj = markRtSbjOrObj; // Mark the record as related independent of whether it is the subject or object of a relationship
244                 }
245
246                 try {
247                         if (markRtSbj != null) {
248                                 repoClient = (NuxeoRepositoryClientImpl) this.getRepositoryClient(ctx);
249                                 NuxeoRepositoryClientImpl nuxeoRepoClient = (NuxeoRepositoryClientImpl) repoClient;
250                                 repoSession = this.getRepositorySession();
251                                 if (repoSession == null) {
252                                         repoSession = repoClient.getRepositorySession(ctx);
253                                         releaseRepoSession = true;
254                                 }
255                         }
256
257                         String commonSchema = getServiceContext().getCommonPartLabel();
258                         extractPagingInfo(commonList, wrapDoc);
259                         List<ListResultField> resultsFields = getListItemsArray(); // Get additional list result fields defined in the service bindings
260                         int baseFields = NUM_STANDARD_LIST_RESULT_FIELDS;
261                         int nFields = resultsFields.size() + NUM_STANDARD_LIST_RESULT_FIELDS;
262                         if (markRtSbj != null) {
263                                 nFields++;
264                                 baseFields++;
265                         }
266                         
267                         String fields[] = new String[nFields]; // REM - Why can't this just be a static array defined once at the top? Then there'd be no need for these hardcoded "[x]" statements and no need for NUM_STANDARD_LIST_RESULT_FIELDS constant as well.
268                         fields[0] = STANDARD_LIST_CSID_FIELD;
269                         fields[1] = STANDARD_LIST_URI_FIELD;
270                         fields[2] = STANDARD_LIST_REFNAME_FIELD;
271                         fields[3] = STANDARD_LIST_UPDATED_AT_FIELD;
272                         fields[4] = STANDARD_LIST_WORKFLOW_FIELD;
273                         
274                         if (markRtSbj != null) {
275                                 fields[5] = STANDARD_LIST_MARK_RT_FIELD;
276                         }
277                         
278                         for (int i = baseFields; i < nFields; i++) {
279                                 ListResultField field = resultsFields.get(i - baseFields);
280                                 fields[i] = field.getElement();
281                         }
282                         commonList.setFieldsReturned(fields);
283                         
284                         Iterator<DocumentModel> iter = wrapDoc.getWrappedObject().iterator();
285                         HashMap<String, Object> item = new HashMap<String, Object>();
286                         while (iter.hasNext()) {
287                                 DocumentModel docModel = iter.next();
288                                 String id = NuxeoUtils.getCsid(docModel);
289                                 item.put(STANDARD_LIST_CSID_FIELD, id);
290                                 
291                                 //
292                                 // If the mark-related query param was set, check to see if the doc we're processing
293                                 // is related to the value specified in the mark-related query param.
294                                 //
295                                 if (markRtSbj != null) {
296                                         String relationClause = RelationsUtils.buildWhereClause(markRtSbj, null, null, id, null, markRtSbj == markRtSbjOrObj);
297                                         String whereClause = relationClause + IQueryManager.SEARCH_QUALIFIER_AND
298                                                         + NuxeoUtils.buildWorkflowNotDeletedWhereClause();
299                                         QueryContext queryContext = new QueryContext(ctx, whereClause);
300                                         queryContext.setDocType(IRelationsManager.DOC_TYPE);
301                                         String query = NuxeoUtils.buildNXQLQuery(queryContext);
302                                         // Search for 1 relation that matches. 1 is enough to fail
303                                         // the filter
304                                         DocumentModelList docList = repoSession.query(query, null, 1, 0, false);
305                                         item.put(STANDARD_LIST_MARK_RT_FIELD, docList.isEmpty() ? "false" : "true");
306                                 }
307                                 
308                                 String uri = getUri(docModel);
309                                 item.put(STANDARD_LIST_URI_FIELD, uri);
310                                 item.put(STANDARD_LIST_REFNAME_FIELD, getRefname(docModel));
311                                 item.put(STANDARD_LIST_UPDATED_AT_FIELD, getUpdatedAtAsString(docModel));
312                                 item.put(STANDARD_LIST_WORKFLOW_FIELD, docModel.getCurrentLifeCycleState());
313
314                                 for (ListResultField field : resultsFields) {
315                                         String schema = field.getSchema();
316                                         if (schema == null || schema.trim().isEmpty()) {
317                                                 schema = commonSchema;
318                                         }
319                                         Object value = getListResultValue(docModel, schema, field);
320                                         if (value != null && value instanceof String) { // If it is String that is either null or empty, we set our value to null
321                                                 String strValue = (String) value;
322                                                 if (strValue.trim().isEmpty() == true) {
323                                                         value = null; // We found an "empty" string value, so just set the value to null so we don't return anything.
324                                                 }
325                                         }
326                                         if (value != null) {
327                                                 item.put(field.getElement(), value);
328                                         }
329                                 }
330                                 commonList.addItem(item);
331                                 item.clear();
332                         }
333                 } catch (Exception e) {
334                         if (logger.isDebugEnabled()) {
335                                 logger.debug("Caught exception ", e);
336                         }
337                         throw new DocumentException(e);
338                 } finally {
339                         // If we got/aquired a new session then we're responsible for releasing it.
340                         if (releaseRepoSession && repoSession != null) {
341                                 repoClient.releaseRepositorySession(ctx, repoSession);
342                         }
343                 }
344
345                 return commonList;
346         }
347
348         // TODO - get rid of this if we can - appears to be unused.
349         @Override
350         public String getQProperty(String prop) throws DocumentException {
351                 ServiceContext ctx = this.getServiceContext();
352                 return ServiceConfigUtils.getDocHandlerParams(ctx).getSchemaName() + ":" + prop;
353         }
354
355         // ============= dublin core handling =======================================
356
357         @Override
358         public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
359                 super.fillAllParts(wrapDoc, action);
360                 fillDublinCoreObject(wrapDoc);
361         }
362
363         /**
364          * Fill dublin core object, but only if there are document handler parameters in the service
365          * bindings.
366          *
367          * @param wrapDoc
368          *            the wrap doc
369          * @throws Exception
370          *             the exception
371          */
372         // TODO - Remove this?
373         // This look like it is never used in a sensible way. It just stuffs a static
374         // String that matches the service name into a bogus field.
375         protected void fillDublinCoreObject(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
376                 DocHandlerParams.Params docHandlerParams = null;
377                 try {
378                         docHandlerParams = ServiceConfigUtils.getDocHandlerParams(getServiceContext());
379                 } catch (Exception e) {
380                         logger.warn(e.getMessage());
381                 }
382
383                 if (docHandlerParams != null) {
384                         String title = docHandlerParams.getDublinCoreTitle();
385                         if (Tools.isEmpty(title) == false) {
386                                 DocumentModel docModel = wrapDoc.getWrappedObject();
387                                 docModel.setPropertyValue("dublincore:title", title);
388                         }
389                 }
390         }
391
392         // ================== UTILITY METHODS ================================================
393         public static ReflectionMapper.STATUS callPropertySetterWithXPathValue(DocumentModel docModel, Object listItem,
394                         String setterName, String schema, String xpath) throws Exception {
395                 // Object prop = docModel.getProperty(label, elementName);
396                 String value = (String) NuxeoUtils.getXPathValue(docModel, schema, xpath);
397                 return ReflectionMapper.callSetter(listItem, setterName, value);
398         }
399
400         public static ReflectionMapper.STATUS callSimplePropertySetter(Object target, String name, Object arg) {
401                 return ReflectionMapper.callSetter(target, name, arg);
402         }
403
404         /**
405          * @param commonListClassname
406          *            is a package-qualified java classname, including inner class $ notation, such as
407          *            "org.collectionspace.services.objectexit.ObjectexitCommonList$ObjectexitListItem".
408          * @param includeStdFields
409          *            set to true to have the method set Uri and Csid automatically, based on id param.
410          */
411         public Object createItemForCommonList(String commonListClassname, DocumentModel docModel, String schema, String id,
412                         boolean includeStdFields) throws Exception {
413                 // createItemForCommonList(docModel, label, id);
414                 Object item = ReflectionMapper.instantiate(commonListClassname);
415                 List<ListResultField> resultsFields = getListItemsArray();
416                 for (ListResultField field : resultsFields) {
417                         callPropertySetterWithXPathValue(docModel, item, field.getSetter(), schema, field.getXpath());
418                 }
419                 if (includeStdFields) {
420                         callSimplePropertySetter(item, "setCsid", id);
421                         callSimplePropertySetter(item, "setUri", getServiceContextPath() + id);
422                 }
423                 
424                 return item;
425         }
426
427         /**
428          * Subclasses should override this method if they don't want to automatically
429          * call List createItemsList(AbstractCommonList commonList, String listItemMethodName)
430          * which will use introspection to create a summary list, and will find the primary
431          * field for you if specified.
432          */
433         public List createItemsList(AbstractCommonList commonList) throws Exception {
434                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
435                 return createItemsList(commonList, ServiceConfigUtils.getDocHandlerParams(ctx).getListResultsItemMethodName());
436         }
437
438         /** e.g. createItemsList(commonList, "getObjectexitListItem" */
439         public List createItemsList(AbstractCommonList commonList, String listItemMethodName) throws Exception {
440                 Class commonListClass = commonList.getClass();
441                 Class[] types = new Class[] {};
442                 try {
443                         Method m = commonListClass.getMethod(listItemMethodName, types);
444                         return (List) (ReflectionMapper.fireGetMethod(m, commonList));
445                 } catch (NoSuchMethodException nsm) {
446                         return new ArrayList();
447                 }
448         }
449
450 }