]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
ebd2323fcf8cdab4a5582906b5a35f25944afff4
[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.relation.nuxeo;
25
26 import java.util.HashMap;
27 import java.util.Iterator;
28
29 import org.collectionspace.services.client.PoxPayloadIn;
30 import org.collectionspace.services.client.PoxPayloadOut;
31 import org.collectionspace.services.common.ResourceBase;
32 import org.collectionspace.services.common.ResourceMap;
33 import org.collectionspace.services.common.ServiceMain;
34 import org.collectionspace.services.common.api.RefName;
35 import org.collectionspace.services.common.api.Tools;
36 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
37 import org.collectionspace.services.common.context.ServiceBindingUtils;
38 import org.collectionspace.services.common.document.DocumentNotFoundException;
39 import org.collectionspace.services.common.document.InvalidDocumentException;
40 import org.collectionspace.services.common.relation.RelationJAXBSchema;
41 import org.collectionspace.services.common.relation.nuxeo.RelationConstants;
42 import org.collectionspace.services.common.context.ServiceContext;
43 import org.collectionspace.services.common.repository.RepositoryClient;
44 import org.collectionspace.services.common.repository.RepositoryClientFactory;
45 import org.collectionspace.services.common.service.ServiceBindingType;
46 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
47 import org.collectionspace.services.relation.RelationsCommon;
48 import org.collectionspace.services.relation.RelationsCommonList;
49 import org.collectionspace.services.relation.RelationsCommonList.RelationListItem;
50
51 // HACK HACK HACK
52 import org.collectionspace.services.client.PersonAuthorityClient;
53 import org.collectionspace.services.client.OrgAuthorityClient;
54 import org.collectionspace.services.client.LocationAuthorityClient;
55 import org.collectionspace.services.client.TaxonomyAuthorityClient;
56 import org.collectionspace.services.client.PlaceAuthorityClient;
57
58 import org.collectionspace.services.common.document.DocumentWrapper;
59 import org.collectionspace.services.jaxb.AbstractCommonList;
60 import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
61 import org.collectionspace.services.nuxeo.client.java.RepositoryJavaClientImpl;
62 import org.collectionspace.services.relation.RelationsDocListItem;
63 import org.nuxeo.ecm.core.api.ClientException;
64 import org.nuxeo.ecm.core.api.CoreSession;
65 import org.nuxeo.ecm.core.api.DocumentModel;
66 import org.nuxeo.ecm.core.api.DocumentModelList;
67 import org.nuxeo.ecm.core.api.model.PropertyException;
68 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 /**
73  * RelationDocumentModelHandler
74  *
75  * $LastChangedRevision: $
76  * $LastChangedDate: $
77  */
78 public class RelationDocumentModelHandler
79         extends RemoteDocumentModelHandlerImpl<RelationsCommon, RelationsCommonList> {
80
81     private final Logger logger = LoggerFactory.getLogger(RelationDocumentModelHandler.class);
82     /**
83      * relation is used to stash JAXB object to use when handle is called
84      * for Action.CREATE, Action.UPDATE or Action.GET
85      */
86     private RelationsCommon relation;
87     /**
88      * relationList is stashed when handle is called
89      * for ACTION.GET_ALL
90      */
91     private RelationsCommonList relationList;
92
93     @Override
94     public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
95         // Merge in the data from the payload
96         super.handleCreate(wrapDoc);
97
98         // And take care of ensuring all the values for the relation info are correct 
99         populateSubjectAndObjectValues(wrapDoc);
100     }
101
102     @Override
103     public void handleUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
104         // Merge in the data from the payload
105         super.handleUpdate(wrapDoc);
106         
107         // And take care of ensuring all the values for the relation info are correct 
108         populateSubjectAndObjectValues(wrapDoc);
109     }
110     
111     private void populateSubjectAndObjectValues(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
112         // Obtain document models for the subject and object of the relation, so that
113         // we ensure we have value docType, URI info. If the docModels support refNames, 
114         // we will also set those.
115         // Note that this introduces another caching problem... 
116         DocumentModel relationDocModel = wrapDoc.getWrappedObject();
117         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
118         RepositoryInstance repoSession = this.getRepositorySession();
119         
120         DocumentModel subjectDocModel = getSubjectOrObjectDocModel(repoSession, relationDocModel, SUBJ_DOC_MODEL);
121         DocumentModel objectDocModel = getSubjectOrObjectDocModel(repoSession, relationDocModel, OBJ_DOC_MODEL);
122
123         // Use values from the subject and object document models to populate the
124         // relevant fields of the relation's own document model.
125         if (subjectDocModel != null) {
126             populateSubjectOrObjectValues(relationDocModel, subjectDocModel, SUBJ_DOC_MODEL);
127         }
128         if (objectDocModel != null) {
129             populateSubjectOrObjectValues(relationDocModel, objectDocModel, OBJ_DOC_MODEL);
130         }
131     }
132
133     @Override
134     public RelationsCommon getCommonPart() {
135         return relation;
136     }
137
138     @Override
139     public void setCommonPart(RelationsCommon theRelation) {
140         this.relation = theRelation;
141     }
142
143     /**get associated Relation (for index/GET_ALL)
144      */
145     @Override
146     public RelationsCommonList getCommonPartList() {
147         return relationList;
148     }
149
150     @Override
151     public void setCommonPartList(RelationsCommonList theRelationList) {
152         this.relationList = theRelationList;
153     }
154
155     @Override
156     public RelationsCommon extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc)
157             throws Exception {
158         throw new UnsupportedOperationException();
159     }
160
161     @Override
162     public void fillCommonPart(RelationsCommon theRelation, DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
163         throw new UnsupportedOperationException();
164     }
165
166     @Override
167     public RelationsCommonList extractCommonPartList(DocumentWrapper<DocumentModelList> wrapDoc) throws Exception {
168         RelationsCommonList relList = this.extractPagingInfo(new RelationsCommonList(), wrapDoc);
169         relList.setFieldsReturned("subjectCsid|relationshipType|predicateDisplayName|objectCsid|uri|csid|subject|object");
170         ServiceContext ctx = getServiceContext();
171         String serviceContextPath = getServiceContextPath();
172
173         TenantBindingConfigReaderImpl tReader = ServiceMain.getInstance().getTenantBindingConfigReader();
174         String serviceName = getServiceContext().getServiceName().toLowerCase();
175         ServiceBindingType sbt = tReader.getServiceBinding(ctx.getTenantId(), serviceName);
176
177         Iterator<DocumentModel> iter = wrapDoc.getWrappedObject().iterator();
178         while (iter.hasNext()) {
179             DocumentModel docModel = iter.next();
180             RelationListItem relListItem = getRelationListItem(ctx, sbt, tReader, docModel, serviceContextPath);
181             relList.getRelationListItem().add(relListItem);
182         }
183         return relList;
184     }
185
186     /** Gets the relation list item, looking up the subject and object documents, and getting summary
187      *  info via the objectName and objectNumber properties in tenant-bindings.
188      * @param ctx the ctx
189      * @param sbt the ServiceBindingType of Relations service
190      * @param tReader the tenant-bindings reader, for looking up docnumber and docname
191      * @param docModel the doc model
192      * @param serviceContextPath the service context path
193      * @return the relation list item, with nested subject and object summary info.
194      * @throws Exception the exception
195      */
196     private RelationListItem getRelationListItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
197             ServiceBindingType sbt,
198             TenantBindingConfigReaderImpl tReader,
199             DocumentModel docModel,
200             String serviceContextPath) throws Exception {
201         RelationListItem relationListItem = new RelationListItem();
202         String id = getCsid(docModel);
203         relationListItem.setCsid(id);
204
205         relationListItem.setSubjectCsid((String) docModel.getProperty(ctx.getCommonPartLabel(), 
206                                                                                                         RelationJAXBSchema.SUBJECT_CSID));
207
208         String predicate = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
209                                                                                                         RelationJAXBSchema.RELATIONSHIP_TYPE);
210         relationListItem.setRelationshipType(predicate);
211         relationListItem.setPredicate(predicate); //predicate is new name for relationshipType.
212         relationListItem.setPredicateDisplayName((String) docModel.getProperty(ctx.getCommonPartLabel(), 
213                                                                                                         RelationJAXBSchema.RELATIONSHIP_TYPE_DISPLAYNAME));
214
215         relationListItem.setObjectCsid((String) docModel.getProperty(ctx.getCommonPartLabel(), 
216                                                                                                         RelationJAXBSchema.OBJECT_CSID));
217
218         relationListItem.setUri(serviceContextPath + id);
219
220         //Now fill in summary info for the related docs: subject and object.
221         String subjectCsid = relationListItem.getSubjectCsid();
222         String subjectDocumentType = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
223                                                                                                         RelationJAXBSchema.SUBJECT_DOCTYPE);
224         RelationsDocListItem subject = createRelationsDocListItem(ctx, sbt, subjectCsid, tReader, subjectDocumentType);
225
226         String subjectUri = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
227                                                                                                         RelationJAXBSchema.SUBJECT_URI);
228         subject.setUri(subjectUri);
229         String subjectRefName = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
230                                                                                                         RelationJAXBSchema.SUBJECT_REFNAME);
231         subject.setRefName(subjectRefName);
232         relationListItem.setSubject(subject);
233
234         String objectCsid = relationListItem.getObjectCsid();
235         String objectDocumentType = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
236                                                                                                         RelationJAXBSchema.OBJECT_DOCTYPE);
237         RelationsDocListItem object = createRelationsDocListItem(ctx, sbt, objectCsid, tReader, objectDocumentType);
238
239         String objectUri = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
240                                                                                                         RelationJAXBSchema.OBJECT_URI);
241         object.setUri(objectUri);
242         String objectRefName = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
243                                                                                                         RelationJAXBSchema.OBJECT_REFNAME);
244         object.setRefName(objectRefName);
245         relationListItem.setObject(object);
246
247         return relationListItem;
248     }
249
250     // DocumentModel itemDocModel = docModelFromCSID(ctx, itemCsid);
251     protected RelationsDocListItem createRelationsDocListItem(
252                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
253             ServiceBindingType sbt,
254             String itemCsid,
255             TenantBindingConfigReaderImpl tReader,
256             String documentType) throws Exception {
257         RelationsDocListItem item = new RelationsDocListItem();
258         item.setDocumentType(documentType);//this one comes from the record, as subjectDocumentType, objectDocumentType.
259         item.setCsid(itemCsid);
260
261         DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(ctx, this.getRepositorySession(), itemCsid);    //null if not found.
262         if (itemDocModel != null) {
263             String itemDocType = itemDocModel.getDocumentType().getName();
264             if (Tools.isBlank(documentType)) {
265                 item.setDocumentType(itemDocType);
266             }
267
268             //TODO: ensure that itemDocType is really the entry point, i.e. servicename==doctype
269             //ServiceBindingType itemSbt2 = tReader.getServiceBinding(ctx.getTenantId(), itemDocType);
270             String propName = "ERROR-FINDING-PROP-VALUE";
271             ServiceBindingType itemSbt = tReader.getServiceBindingForDocType(ctx.getTenantId(), itemDocType);
272             try {
273                 propName = ServiceBindingUtils.getPropertyValue(itemSbt, ServiceBindingUtils.OBJ_NAME_PROP);
274                 String itemDocname = ServiceBindingUtils.getMappedFieldInDoc(itemSbt, ServiceBindingUtils.OBJ_NAME_PROP, itemDocModel);
275                 if (propName == null || itemDocname == null) {
276                 } else {
277                     item.setName(itemDocname);
278                 }
279             } catch (Throwable t) {
280                 logger.error("====Error finding objectNameProperty: " + itemDocModel + " field " + ServiceBindingUtils.OBJ_NAME_PROP + "=" + propName
281                         + " not found in itemDocType: " + itemDocType + " inner: " + t.getMessage());
282             }
283             propName = "ERROR-FINDING-PROP-VALUE";
284             try {
285                 propName = ServiceBindingUtils.getPropertyValue(itemSbt, ServiceBindingUtils.OBJ_NUMBER_PROP);
286                 String itemDocnumber = ServiceBindingUtils.getMappedFieldInDoc(itemSbt, ServiceBindingUtils.OBJ_NUMBER_PROP, itemDocModel);
287
288                 if (propName == null || itemDocnumber == null) {
289                 } else {
290                     item.setNumber(itemDocnumber);
291                 }
292             } catch (Throwable t) {
293                 logger.error("====Error finding objectNumberProperty: " + ServiceBindingUtils.OBJ_NUMBER_PROP + "=" + propName
294                         + " not found in itemDocType: " + itemDocType + " inner: " + t.getMessage());
295             }
296         } else {
297             item.setError("INVALID: related object is absent");
298             // Laramie20110510 CSPACE-3739  throw the exception for 3739, otherwise, don't throw it.
299             //throw new Exception("INVALID: related object is absent "+itemCsid);
300         }
301         return item;
302     }
303
304     @Override
305     public String getQProperty(String prop) {
306         return "/" + RelationConstants.NUXEO_SCHEMA_ROOT_ELEMENT + "/" + prop;
307     }
308
309     private final boolean SUBJ_DOC_MODEL = true;
310     private final boolean OBJ_DOC_MODEL = false;
311     
312     private DocumentModel getSubjectOrObjectDocModel(
313                 RepositoryInstance repoSession,
314                 DocumentModel relationDocModel,
315                 boolean fSubject) throws Exception {
316         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
317         
318         // Get the document model for the object of the relation.
319         String commonPartLabel = ctx.getCommonPartLabel();
320         String csid = "";
321         String refName = "";
322         DocumentModel docModel = null;
323         // FIXME: Currently assumes that the object CSID is valid if present
324         // in the incoming payload.
325         try {
326             csid = (String) relationDocModel.getProperty(commonPartLabel, 
327                         (fSubject?RelationJAXBSchema.SUBJECT_CSID:RelationJAXBSchema.OBJECT_CSID));
328         } catch (PropertyException pe) {
329             // Per CSPACE-4468, ignore any property exception here.
330             // The objectCsid and/or subjectCsid field in a relation record
331             // can now be null (missing), because a refName value can be
332             // provided as an alternate identifier.
333         }
334         if (Tools.notBlank(csid)) {
335                 RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl)getRepositoryClient(ctx);
336             DocumentWrapper<DocumentModel> docWrapper = nuxeoRepoClient.getDocFromCsid(ctx, repoSession, csid);
337             docModel = docWrapper.getWrappedObject();
338         } else { //  if (Tools.isBlank(objectCsid)) {
339             try {
340                 refName = (String) relationDocModel.getProperty(commonPartLabel, 
341                                 (fSubject?RelationJAXBSchema.SUBJECT_REFNAME:RelationJAXBSchema.OBJECT_REFNAME));
342                 docModel = ResourceBase.getDocModelForRefName(repoSession, refName, ctx.getResourceMap());
343             } catch (Exception e) {
344                 throw new InvalidDocumentException(
345                         "Relation record must have a CSID or refName to identify the object of the relation.", e);
346             }
347         }
348         if(docModel==null) {
349                 throw new DocumentNotFoundException("Relation.getSubjectOrObjectDocModel could not find doc with CSID: "
350                                         +csid+" and/or refName: "+refName );
351         }
352         return docModel;
353     }
354     
355     private void populateSubjectOrObjectValues(
356                 DocumentModel relationDocModel, 
357                 DocumentModel subjectOrObjectDocModel,
358                 boolean fSubject ) {
359         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
360         
361         HashMap<String,Object> properties = new HashMap<String,Object>();
362         try {
363                 String doctype = (String) subjectOrObjectDocModel.getType();
364                 properties.put((fSubject?RelationJAXBSchema.SUBJECT_DOCTYPE:RelationJAXBSchema.OBJECT_DOCTYPE),
365                                                         doctype);
366         
367                 String csid = (String) subjectOrObjectDocModel.getName();
368                 properties.put((fSubject?RelationJAXBSchema.SUBJECT_CSID:RelationJAXBSchema.OBJECT_CSID),
369                                                         csid);
370         
371                 String uri = (String) subjectOrObjectDocModel.getProperty(COLLECTIONSPACE_CORE_SCHEMA,
372                                                                                                                                         COLLECTIONSPACE_CORE_URI);
373                 properties.put((fSubject?RelationJAXBSchema.SUBJECT_URI:RelationJAXBSchema.OBJECT_URI),
374                                                         uri);
375                 
376                 String common_schema = getCommonSchemaNameForDocType(doctype);
377                 
378                 if(common_schema!=null) {
379                         String refname = (String)subjectOrObjectDocModel.getProperty(common_schema, 
380                                                                                                                         RefName.REFNAME );
381                     properties.put((fSubject?RelationJAXBSchema.SUBJECT_REFNAME:RelationJAXBSchema.OBJECT_REFNAME),
382                                 refname);
383                 }
384         } catch (ClientException ce) {
385             throw new RuntimeException(
386                     "populateSubjectOrObjectValues: Problem fetching field " + ce.getLocalizedMessage());
387         }
388
389         // FIXME: Call below is based solely on Nuxeo API docs; have not yet verified that it correctly updates existing
390         // property values in the target document model.
391         try {
392                 relationDocModel.setProperties(ctx.getCommonPartLabel(), properties);
393         } catch (ClientException ce) {
394             throw new RuntimeException(
395                     "populateSubjectValues: Problem setting fields " + ce.getLocalizedMessage());
396         }
397     }
398     
399     private String getCommonSchemaNameForDocType(String docType) {
400         String common_schema = null;
401         if(docType!=null) {
402                 // HACK - Use startsWith to allow for extension of schemas.
403                 if(docType.startsWith("Person"))
404                         common_schema = PersonAuthorityClient.SERVICE_ITEM_COMMON_PART_NAME;
405                 else if(docType.startsWith("Organization"))
406                         common_schema = OrgAuthorityClient.SERVICE_ITEM_COMMON_PART_NAME;
407                 else if(docType.startsWith("Locationitem"))
408                         common_schema = LocationAuthorityClient.SERVICE_ITEM_COMMON_PART_NAME;
409                 else if(docType.startsWith("Taxon"))
410                         common_schema = TaxonomyAuthorityClient.SERVICE_ITEM_COMMON_PART_NAME;
411                 else if(docType.startsWith("Placeitem"))
412                         common_schema = PlaceAuthorityClient.SERVICE_ITEM_COMMON_PART_NAME;
413                 //else leave it null.
414         }
415         return common_schema;
416     }
417
418 }