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:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
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.
24 package org.collectionspace.services.relation.nuxeo;
26 import java.util.HashMap;
27 import java.util.Iterator;
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;
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;
57 import org.collectionspace.services.common.document.DocumentWrapper;
58 import org.collectionspace.services.jaxb.AbstractCommonList;
59 import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
60 import org.collectionspace.services.relation.RelationsDocListItem;
61 import org.nuxeo.ecm.core.api.ClientException;
62 import org.nuxeo.ecm.core.api.DocumentModel;
63 import org.nuxeo.ecm.core.api.DocumentModelList;
64 import org.nuxeo.ecm.core.api.model.PropertyException;
65 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
70 * RelationDocumentModelHandler
72 * $LastChangedRevision: $
75 public class RelationDocumentModelHandler
76 extends RemoteDocumentModelHandlerImpl<RelationsCommon, RelationsCommonList> {
78 private final Logger logger = LoggerFactory.getLogger(RelationDocumentModelHandler.class);
80 * relation is used to stash JAXB object to use when handle is called
81 * for Action.CREATE, Action.UPDATE or Action.GET
83 private RelationsCommon relation;
85 * relationList is stashed when handle is called
88 private RelationsCommonList relationList;
91 public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
92 // Merge in the data from the payload
93 super.handleCreate(wrapDoc);
95 // And take care of ensuring all the values for the relation info are correct
96 populateSubjectAndObjectValues(wrapDoc);
100 public void handleUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
101 // Merge in the data from the payload
102 super.handleUpdate(wrapDoc);
104 // And take care of ensuring all the values for the relation info are correct
105 populateSubjectAndObjectValues(wrapDoc);
108 private void populateSubjectAndObjectValues(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
109 // Obtain document models for the subject and object of the relation, so that
110 // we ensure we have value docType, URI info. If the docModels support refNames,
111 // we will also set those.
112 // Note that this introduces another caching problem...
113 DocumentModel relationDocModel = wrapDoc.getWrappedObject();
114 ServiceContext ctx = getServiceContext();
115 DocumentModel subjectDocModel = getSubjectOrObjectDocModel(relationDocModel, ctx, SUBJ_DOC_MODEL);
116 DocumentModel objectDocModel = getSubjectOrObjectDocModel(relationDocModel, ctx, OBJ_DOC_MODEL);
118 // Use values from the subject and object document models to populate the
119 // relevant fields of the relation's own document model.
120 if (subjectDocModel != null) {
121 populateSubjectOrObjectValues(relationDocModel, subjectDocModel, ctx, SUBJ_DOC_MODEL);
123 if (objectDocModel != null) {
124 populateSubjectOrObjectValues(relationDocModel, objectDocModel, ctx, OBJ_DOC_MODEL);
129 public RelationsCommon getCommonPart() {
134 public void setCommonPart(RelationsCommon theRelation) {
135 this.relation = theRelation;
138 /**get associated Relation (for index/GET_ALL)
141 public RelationsCommonList getCommonPartList() {
146 public void setCommonPartList(RelationsCommonList theRelationList) {
147 this.relationList = theRelationList;
151 public RelationsCommon extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc)
153 throw new UnsupportedOperationException();
157 public void fillCommonPart(RelationsCommon theRelation, DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
158 throw new UnsupportedOperationException();
162 public RelationsCommonList extractCommonPartList(DocumentWrapper<DocumentModelList> wrapDoc) throws Exception {
163 RelationsCommonList relList = this.extractPagingInfo(new RelationsCommonList(), wrapDoc);
164 relList.setFieldsReturned("subjectCsid|relationshipType|predicateDisplayName|objectCsid|uri|csid|subject|object");
165 ServiceContext ctx = getServiceContext();
166 String serviceContextPath = getServiceContextPath();
168 TenantBindingConfigReaderImpl tReader = ServiceMain.getInstance().getTenantBindingConfigReader();
169 String serviceName = getServiceContext().getServiceName().toLowerCase();
170 ServiceBindingType sbt = tReader.getServiceBinding(ctx.getTenantId(), serviceName);
172 Iterator<DocumentModel> iter = wrapDoc.getWrappedObject().iterator();
173 while (iter.hasNext()) {
174 DocumentModel docModel = iter.next();
175 RelationListItem relListItem = getRelationListItem(ctx, sbt, tReader, docModel, serviceContextPath);
176 relList.getRelationListItem().add(relListItem);
181 /** Gets the relation list item, looking up the subject and object documents, and getting summary
182 * info via the objectName and objectNumber properties in tenant-bindings.
184 * @param sbt the ServiceBindingType of Relations service
185 * @param tReader the tenant-bindings reader, for looking up docnumber and docname
186 * @param docModel the doc model
187 * @param serviceContextPath the service context path
188 * @return the relation list item, with nested subject and object summary info.
189 * @throws Exception the exception
191 private RelationListItem getRelationListItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
192 ServiceBindingType sbt,
193 TenantBindingConfigReaderImpl tReader,
194 DocumentModel docModel,
195 String serviceContextPath) throws Exception {
196 RelationListItem relationListItem = new RelationListItem();
197 String id = getCsid(docModel);
198 relationListItem.setCsid(id);
200 relationListItem.setSubjectCsid((String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.DOCUMENT_ID_1));
202 String predicate = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.RELATIONSHIP_TYPE);
203 relationListItem.setRelationshipType(predicate);
204 relationListItem.setPredicate(predicate); //predicate is new name for relationshipType.
205 relationListItem.setPredicateDisplayName((String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.RELATIONSHIP_TYPE_DISPLAYNAME));
207 relationListItem.setObjectCsid((String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.DOCUMENT_ID_2));
209 relationListItem.setUri(serviceContextPath + id);
211 //Now fill in summary info for the related docs: subject and object.
212 String subjectCsid = relationListItem.getSubjectCsid();
213 String documentType = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.DOCUMENT_TYPE_1);
214 RelationsDocListItem subject = createRelationsDocListItem(ctx, sbt, subjectCsid, tReader, documentType);
216 //Object o1 = docModel.getProperty(ctx.getCommonPartLabel(), "subject");
217 //Object o2 = docModel.getProperty(ctx.getCommonPartLabel(), "object");
219 String subjectUri = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.SUBJECT_URI);
220 subject.setUri(subjectUri);
221 String subjectRefName = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.SUBJECT_REFNAME);
222 subject.setRefName(subjectRefName);
223 relationListItem.setSubject(subject);
225 String objectCsid = relationListItem.getObjectCsid();
226 String documentType2 = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.DOCUMENT_TYPE_2);
227 RelationsDocListItem object = createRelationsDocListItem(ctx, sbt, objectCsid, tReader, documentType2);
229 String objectUri = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.OBJECT_URI);
230 object.setUri(objectUri);
231 String objectRefName = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.OBJECT_REFNAME);
232 object.setRefName(objectRefName);
233 relationListItem.setObject(object);
235 return relationListItem;
238 // DocumentModel itemDocModel = docModelFromCSID(ctx, itemCsid);
239 protected RelationsDocListItem createRelationsDocListItem(ServiceContext ctx,
240 ServiceBindingType sbt,
242 TenantBindingConfigReaderImpl tReader,
243 String documentType) throws Exception {
244 RelationsDocListItem item = new RelationsDocListItem();
245 item.setDocumentType(documentType);//this one comes from the record, as documentType1, documentType2.
246 // CSPACE-4037 REMOVING: item.setService(documentType);//this one comes from the record, as documentType1, documentType2. Current app seems to use servicename for this.
247 item.setCsid(itemCsid);
249 DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, itemCsid); //null if not found.
250 if (itemDocModel != null) {
251 String itemDocType = itemDocModel.getDocumentType().getName();
252 // CSPACE-4037 REMOVING: item.setDocumentTypeFromModel(itemDocType); //this one comes from the nuxeo documentType
254 //DEBUG: System.out.println("\r\n******** AuthorityItemDocumentModelHandlder documentType **************\r\n\tdocModel: "+itemDocType+"\r\n\tpayload: "+documentType);
255 //boolean usedDocumentTypeFromPayload = true;
256 /*if ( ! Tools.isBlank(documentType)){
257 if (documentType.equals(itemDocType)){
258 //usedDocumentTypeFromPayload = true;
260 // Laramie20110510 CSPACE-3739 throw the exception for 3739, otherwise, don't throw it.
261 //throw new Exception("documentType supplied was wrong. supplied: "+documentType+" required: "+itemDocType+ " itemCsid: "+itemCsid );
264 //usedDocumentTypeFromPayload = false;
265 item.setDocumentType(itemDocType);
267 if (Tools.isBlank(documentType)) {
268 item.setDocumentType(itemDocType);
271 // TODO: clean all the output statements out of here when CSPACE-4037 is done.
272 //TODO: ensure that itemDocType is really the entry point, i.e. servicename==doctype
273 //ServiceBindingType itemSbt2 = tReader.getServiceBinding(ctx.getTenantId(), itemDocType);
274 String propName = "ERROR-FINDING-PROP-VALUE";
275 ServiceBindingType itemSbt = tReader.getServiceBindingForDocType(ctx.getTenantId(), itemDocType);
277 propName = ServiceBindingUtils.getPropertyValue(itemSbt, ServiceBindingUtils.OBJ_NAME_PROP);
278 String itemDocname = ServiceBindingUtils.getMappedFieldInDoc(itemSbt, ServiceBindingUtils.OBJ_NAME_PROP, itemDocModel);
279 if (propName == null || itemDocname == null) {
280 //System.out.println("=== prop NOT found: "+ServiceBindingUtils.OBJ_NAME_PROP+"::"+propName+"="+itemDocname+" documentType: "+documentType);
282 item.setName(itemDocname);
283 //System.out.println("=== found prop : "+ServiceBindingUtils.OBJ_NAME_PROP+"::"+propName+"="+itemDocname+" documentType: "+documentType);
285 } catch (Throwable t) {
286 System.out.println("====Error finding objectNameProperty: " + itemDocModel + " field " + ServiceBindingUtils.OBJ_NAME_PROP + "=" + propName
287 + " not found in itemDocType: " + itemDocType + " inner: " + t.getMessage());
289 propName = "ERROR-FINDING-PROP-VALUE";
291 propName = ServiceBindingUtils.getPropertyValue(itemSbt, ServiceBindingUtils.OBJ_NUMBER_PROP);
292 String itemDocnumber = ServiceBindingUtils.getMappedFieldInDoc(itemSbt, ServiceBindingUtils.OBJ_NUMBER_PROP, itemDocModel);
294 if (propName == null || itemDocnumber == null) {
295 //System.out.println("=== prop NOT found: "+ServiceBindingUtils.OBJ_NUMBER_PROP+"::"+propName+"="+itemDocnumber
296 // +" documentType: "+documentType);
298 item.setNumber(itemDocnumber);
299 //System.out.println("============ found prop : "+ServiceBindingUtils.OBJ_NUMBER_PROP+"::"+propName+"="+itemDocnumber
300 // +" documentType: "+documentType);
302 } catch (Throwable t) {
303 logger.error("====Error finding objectNumberProperty: " + ServiceBindingUtils.OBJ_NUMBER_PROP + "=" + propName
304 + " not found in itemDocType: " + itemDocType + " inner: " + t.getMessage());
307 item.setError("INVALID: related object is absent");
308 // Laramie20110510 CSPACE-3739 throw the exception for 3739, otherwise, don't throw it.
309 //throw new Exception("INVALID: related object is absent "+itemCsid);
315 public String getQProperty(String prop) {
316 return "/" + RelationConstants.NUXEO_SCHEMA_ROOT_ELEMENT + "/" + prop;
319 private final boolean SUBJ_DOC_MODEL = true;
320 private final boolean OBJ_DOC_MODEL = false;
322 private DocumentModel getSubjectOrObjectDocModel(
323 DocumentModel relationDocModel, ServiceContext ctx, boolean fSubject) throws Exception {
324 // Get the document model for the object of the relation.
325 String commonPartLabel = ctx.getCommonPartLabel();
328 DocumentModel docModel = null;
329 // FIXME: Currently assumes that the object CSID is valid if present
330 // in the incoming payload.
332 csid = (String) relationDocModel.getProperty(commonPartLabel,
333 (fSubject?RelationJAXBSchema.SUBJECT_CSID:RelationJAXBSchema.OBJECT_CSID));
334 // FIXME: Remove this entire 'if' statement when legacy fields are removed from the Relation record:
335 if (Tools.isBlank(csid)) {
336 csid = (String) relationDocModel.getProperty(commonPartLabel,
337 (fSubject?RelationJAXBSchema.DOCUMENT_ID_1:RelationJAXBSchema.DOCUMENT_ID_2));
339 } catch (PropertyException pe) {
340 // Per CSPACE-4468, ignore any property exception here.
341 // The objectCsid and/or subjectCsid field in a relation record
342 // can now be null (missing), because a refName value can be
343 // provided as an alternate identifier.
345 if (Tools.notBlank(csid)) {
346 DocumentWrapper<DocumentModel> docWrapper = getRepositoryClient(ctx).getDocFromCsid(ctx, csid);
347 docModel = docWrapper.getWrappedObject();
348 } else { // if (Tools.isBlank(objectCsid)) {
350 refName = (String) relationDocModel.getProperty(commonPartLabel,
351 (fSubject?RelationJAXBSchema.SUBJECT_REFNAME:RelationJAXBSchema.OBJECT_REFNAME));
352 docModel = ResourceBase.getDocModelForRefName(refName, ctx.getResourceMap());
353 } catch (Exception e) {
354 throw new InvalidDocumentException(
355 "Relation record must have a CSID or refName to identify the object of the relation.", e);
359 throw new DocumentNotFoundException("Relation.getSubjectOrObjectDocModel could not find doc with CSID: "
360 +csid+" and/or refName: "+refName );
365 private void populateSubjectOrObjectValues(
366 DocumentModel relationDocModel,
367 DocumentModel subjectOrObjectDocModel,
370 HashMap<String,Object> properties = new HashMap<String,Object>();
373 String doctype = (String) subjectOrObjectDocModel.getType();
374 properties.put((fSubject?RelationJAXBSchema.SUBJECT_DOCTYPE:RelationJAXBSchema.OBJECT_DOCTYPE),
376 // FIXME: Remove the line below when legacy fields are removed from the Relation record:
377 properties.put((fSubject?RelationJAXBSchema.DOCUMENT_TYPE_1:RelationJAXBSchema.DOCUMENT_TYPE_2),
380 String csid = (String) subjectOrObjectDocModel.getName();
381 properties.put((fSubject?RelationJAXBSchema.SUBJECT_CSID:RelationJAXBSchema.OBJECT_CSID),
383 // FIXME: Remove the two lines immediately below when legacy fields are removed from the Relation record:
384 properties.put((fSubject?RelationJAXBSchema.DOCUMENT_ID_1:RelationJAXBSchema.DOCUMENT_ID_2),
387 String uri = (String) subjectOrObjectDocModel.getProperty(COLLECTIONSPACE_CORE_SCHEMA,
388 COLLECTIONSPACE_CORE_URI);
389 properties.put((fSubject?RelationJAXBSchema.SUBJECT_URI:RelationJAXBSchema.OBJECT_URI),
392 String common_schema = getCommonSchemaNameForDocType(doctype);
394 if(common_schema!=null) {
395 String refname = (String)subjectOrObjectDocModel.getProperty(common_schema,
397 properties.put((fSubject?RelationJAXBSchema.SUBJECT_REFNAME:RelationJAXBSchema.OBJECT_REFNAME),
400 } catch (ClientException ce) {
401 throw new RuntimeException(
402 "populateSubjectOrObjectValues: Problem fetching field " + ce.getLocalizedMessage());
405 // FIXME: Call below is based solely on Nuxeo API docs; have not yet verified that it correctly updates existing
406 // property values in the target document model.
408 relationDocModel.setProperties(ctx.getCommonPartLabel(), properties);
409 } catch (ClientException ce) {
410 throw new RuntimeException(
411 "populateSubjectValues: Problem setting fields " + ce.getLocalizedMessage());
415 private String getCommonSchemaNameForDocType(String docType) {
416 String common_schema = null;
417 if("Person".equals(docType))
418 common_schema = PersonAuthorityClient.SERVICE_ITEM_COMMON_PART_NAME;
419 else if("Organization".equals(docType))
420 common_schema = OrgAuthorityClient.SERVICE_ITEM_COMMON_PART_NAME;
421 else if("Locationitem".equals(docType))
422 common_schema = LocationAuthorityClient.SERVICE_ITEM_COMMON_PART_NAME;
423 else if("Taxon".equals(docType))
424 common_schema = TaxonomyAuthorityClient.SERVICE_ITEM_COMMON_PART_NAME;
425 //else leave it null.
426 return common_schema;