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.Iterator;
28 import org.collectionspace.services.client.PoxPayloadIn;
29 import org.collectionspace.services.client.PoxPayloadOut;
30 import org.collectionspace.services.common.ServiceMain;
31 import org.collectionspace.services.common.api.Tools;
32 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
33 import org.collectionspace.services.common.context.ServiceBindingUtils;
34 import org.collectionspace.services.common.document.InvalidDocumentException;
35 import org.collectionspace.services.common.relation.RelationJAXBSchema;
36 import org.collectionspace.services.common.relation.nuxeo.RelationConstants;
37 import org.collectionspace.services.common.context.ServiceContext;
38 import org.collectionspace.services.common.repository.RepositoryClient;
39 import org.collectionspace.services.common.repository.RepositoryClientFactory;
40 import org.collectionspace.services.common.service.ServiceBindingType;
41 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
42 import org.collectionspace.services.relation.RelationsCommon;
43 import org.collectionspace.services.relation.RelationsCommonList;
44 import org.collectionspace.services.relation.RelationsCommonList.RelationListItem;
46 import org.collectionspace.services.common.document.DocumentWrapper;
47 import org.collectionspace.services.jaxb.AbstractCommonList;
48 import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
49 import org.collectionspace.services.relation.RelationsDocListItem;
50 import org.nuxeo.ecm.core.api.DocumentModel;
51 import org.nuxeo.ecm.core.api.DocumentModelList;
52 import org.nuxeo.ecm.core.api.model.PropertyException;
53 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
58 * RelationDocumentModelHandler
60 * $LastChangedRevision: $
63 public class RelationDocumentModelHandler
64 extends RemoteDocumentModelHandlerImpl<RelationsCommon, RelationsCommonList> {
66 private final Logger logger = LoggerFactory.getLogger(RelationDocumentModelHandler.class);
68 * relation is used to stash JAXB object to use when handle is called
69 * for Action.CREATE, Action.UPDATE or Action.GET
71 private RelationsCommon relation;
73 * relationList is stashed when handle is called
76 private RelationsCommonList relationList;
79 public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
81 // Obtain document models for the subject and object of the relation.
82 DocumentModel relationDocModel = wrapDoc.getWrappedObject();
83 ServiceContext ctx = getServiceContext();
84 DocumentModel subjectDocModel = getSubjectDocModel(relationDocModel, ctx);
85 DocumentModel objectDocModel = getObjectDocModel(relationDocModel, ctx);
87 // Use values from the subject and object document models to populate the
88 // relevant fields of the relation's own document model.
89 if (subjectDocModel != null) {
90 relationDocModel = populateSubjectValues(relationDocModel, subjectDocModel);
92 if (objectDocModel != null) {
93 relationDocModel = populateObjectValues(relationDocModel, objectDocModel);
96 // FIXME: Verify the following:
97 // Do we call this method here, only after we've updated the relationDocModel?
98 // Has the wrapDoc instance itself been updated, in the process of updating the relationDocModel,
99 // or do we need to pass the updated relationDocModel back into that instance?
100 super.handleCreate(wrapDoc);
105 public void handleUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
106 super.handleUpdate(wrapDoc);
110 public RelationsCommon getCommonPart() {
115 public void setCommonPart(RelationsCommon theRelation) {
116 this.relation = theRelation;
119 /**get associated Relation (for index/GET_ALL)
122 public RelationsCommonList getCommonPartList() {
127 public void setCommonPartList(RelationsCommonList theRelationList) {
128 this.relationList = theRelationList;
132 public RelationsCommon extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc)
134 throw new UnsupportedOperationException();
138 public void fillCommonPart(RelationsCommon theRelation, DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
139 throw new UnsupportedOperationException();
143 public RelationsCommonList extractCommonPartList(DocumentWrapper<DocumentModelList> wrapDoc) throws Exception {
144 RelationsCommonList relList = this.extractPagingInfo(new RelationsCommonList(), wrapDoc);
145 relList.setFieldsReturned("subjectCsid|relationshipType|predicateDisplayName|objectCsid|uri|csid|subject|object");
146 ServiceContext ctx = getServiceContext();
147 String serviceContextPath = getServiceContextPath();
149 TenantBindingConfigReaderImpl tReader = ServiceMain.getInstance().getTenantBindingConfigReader();
150 String serviceName = getServiceContext().getServiceName().toLowerCase();
151 ServiceBindingType sbt = tReader.getServiceBinding(ctx.getTenantId(), serviceName);
153 Iterator<DocumentModel> iter = wrapDoc.getWrappedObject().iterator();
154 while (iter.hasNext()) {
155 DocumentModel docModel = iter.next();
156 RelationListItem relListItem = getRelationListItem(ctx, sbt, tReader, docModel, serviceContextPath);
157 relList.getRelationListItem().add(relListItem);
162 /** Gets the relation list item, looking up the subject and object documents, and getting summary
163 * info via the objectName and objectNumber properties in tenant-bindings.
165 * @param sbt the ServiceBindingType of Relations service
166 * @param tReader the tenant-bindings reader, for looking up docnumber and docname
167 * @param docModel the doc model
168 * @param serviceContextPath the service context path
169 * @return the relation list item, with nested subject and object summary info.
170 * @throws Exception the exception
172 private RelationListItem getRelationListItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
173 ServiceBindingType sbt,
174 TenantBindingConfigReaderImpl tReader,
175 DocumentModel docModel,
176 String serviceContextPath) throws Exception {
177 RelationListItem relationListItem = new RelationListItem();
178 String id = getCsid(docModel);
179 relationListItem.setCsid(id);
181 relationListItem.setSubjectCsid((String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.DOCUMENT_ID_1));
183 String predicate = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.RELATIONSHIP_TYPE);
184 relationListItem.setRelationshipType(predicate);
185 relationListItem.setPredicate(predicate); //predicate is new name for relationshipType.
186 relationListItem.setPredicateDisplayName((String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.RELATIONSHIP_TYPE_DISPLAYNAME));
188 relationListItem.setObjectCsid((String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.DOCUMENT_ID_2));
190 relationListItem.setUri(serviceContextPath + id);
192 //Now fill in summary info for the related docs: subject and object.
193 String subjectCsid = relationListItem.getSubjectCsid();
194 String documentType = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.DOCUMENT_TYPE_1);
195 RelationsDocListItem subject = createRelationsDocListItem(ctx, sbt, subjectCsid, tReader, documentType);
197 //Object o1 = docModel.getProperty(ctx.getCommonPartLabel(), "subject");
198 //Object o2 = docModel.getProperty(ctx.getCommonPartLabel(), "object");
200 String subjectUri = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.SUBJECT_URI);
201 subject.setUri(subjectUri);
202 relationListItem.setSubject(subject);
204 String objectCsid = relationListItem.getObjectCsid();
205 String documentType2 = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.DOCUMENT_TYPE_2);
206 RelationsDocListItem object = createRelationsDocListItem(ctx, sbt, objectCsid, tReader, documentType2);
208 String objectUri = (String) docModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.OBJECT_URI);
209 object.setUri(objectUri);
210 relationListItem.setObject(object);
212 return relationListItem;
215 // DocumentModel itemDocModel = docModelFromCSID(ctx, itemCsid);
216 protected RelationsDocListItem createRelationsDocListItem(ServiceContext ctx,
217 ServiceBindingType sbt,
219 TenantBindingConfigReaderImpl tReader,
220 String documentType) throws Exception {
221 RelationsDocListItem item = new RelationsDocListItem();
222 item.setDocumentType(documentType);//this one comes from the record, as documentType1, documentType2.
223 // CSPACE-4037 REMOVING: item.setService(documentType);//this one comes from the record, as documentType1, documentType2. Current app seems to use servicename for this.
224 item.setCsid(itemCsid);
226 DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, itemCsid); //null if not found.
227 if (itemDocModel != null) {
228 String itemDocType = itemDocModel.getDocumentType().getName();
229 // CSPACE-4037 REMOVING: item.setDocumentTypeFromModel(itemDocType); //this one comes from the nuxeo documentType
231 //DEBUG: System.out.println("\r\n******** AuthorityItemDocumentModelHandlder documentType **************\r\n\tdocModel: "+itemDocType+"\r\n\tpayload: "+documentType);
232 //boolean usedDocumentTypeFromPayload = true;
233 /*if ( ! Tools.isBlank(documentType)){
234 if (documentType.equals(itemDocType)){
235 //usedDocumentTypeFromPayload = true;
237 // Laramie20110510 CSPACE-3739 throw the exception for 3739, otherwise, don't throw it.
238 //throw new Exception("documentType supplied was wrong. supplied: "+documentType+" required: "+itemDocType+ " itemCsid: "+itemCsid );
241 //usedDocumentTypeFromPayload = false;
242 item.setDocumentType(itemDocType);
244 if (Tools.isBlank(documentType)) {
245 item.setDocumentType(itemDocType);
248 // TODO: clean all the output statements out of here when CSPACE-4037 is done.
249 //TODO: ensure that itemDocType is really the entry point, i.e. servicename==doctype
250 //ServiceBindingType itemSbt2 = tReader.getServiceBinding(ctx.getTenantId(), itemDocType);
251 String propName = "ERROR-FINDING-PROP-VALUE";
252 ServiceBindingType itemSbt = tReader.getServiceBindingForDocType(ctx.getTenantId(), itemDocType);
254 propName = ServiceBindingUtils.getPropertyValue(itemSbt, ServiceBindingUtils.OBJ_NAME_PROP);
255 String itemDocname = ServiceBindingUtils.getMappedFieldInDoc(itemSbt, ServiceBindingUtils.OBJ_NAME_PROP, itemDocModel);
256 if (propName == null || itemDocname == null) {
257 //System.out.println("=== prop NOT found: "+ServiceBindingUtils.OBJ_NAME_PROP+"::"+propName+"="+itemDocname+" documentType: "+documentType);
259 item.setName(itemDocname);
260 //System.out.println("=== found prop : "+ServiceBindingUtils.OBJ_NAME_PROP+"::"+propName+"="+itemDocname+" documentType: "+documentType);
262 } catch (Throwable t) {
263 System.out.println("====Error finding objectNameProperty: " + itemDocModel + " field " + ServiceBindingUtils.OBJ_NAME_PROP + "=" + propName
264 + " not found in itemDocType: " + itemDocType + " inner: " + t.getMessage());
266 propName = "ERROR-FINDING-PROP-VALUE";
268 propName = ServiceBindingUtils.getPropertyValue(itemSbt, ServiceBindingUtils.OBJ_NUMBER_PROP);
269 String itemDocnumber = ServiceBindingUtils.getMappedFieldInDoc(itemSbt, ServiceBindingUtils.OBJ_NUMBER_PROP, itemDocModel);
271 if (propName == null || itemDocnumber == null) {
272 //System.out.println("=== prop NOT found: "+ServiceBindingUtils.OBJ_NUMBER_PROP+"::"+propName+"="+itemDocnumber
273 // +" documentType: "+documentType);
275 item.setNumber(itemDocnumber);
276 //System.out.println("============ found prop : "+ServiceBindingUtils.OBJ_NUMBER_PROP+"::"+propName+"="+itemDocnumber
277 // +" documentType: "+documentType);
279 } catch (Throwable t) {
280 logger.error("====Error finding objectNumberProperty: " + ServiceBindingUtils.OBJ_NUMBER_PROP + "=" + propName
281 + " not found in itemDocType: " + itemDocType + " inner: " + t.getMessage());
284 item.setError("INVALID: related object is absent");
285 // Laramie20110510 CSPACE-3739 throw the exception for 3739, otherwise, don't throw it.
286 //throw new Exception("INVALID: related object is absent "+itemCsid);
292 public String getQProperty(String prop) {
293 return "/" + RelationConstants.NUXEO_SCHEMA_ROOT_ELEMENT + "/" + prop;
297 * Obtains the subject resource and uses its values to populate
298 * subject-related fields in the relation resource.
300 private DocumentModel getSubjectDocModel(DocumentModel relationDocModel, ServiceContext ctx) throws Exception {
301 // Get the document model for the subject of the relation.
302 DocumentModel subjectDocModel = null;
303 String subjectCsid = "";
304 String subjectRefName = "";
305 // FIXME: Currently assumes that the object CSID is valid if present
306 // in the incoming payload.
308 subjectCsid = (String) relationDocModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.SUBJECT_CSID);
309 // FIXME: Remove this entire 'if' statement when legacy fields are removed from the Relation record:
310 if (Tools.isBlank(subjectCsid)) {
311 subjectCsid = (String) relationDocModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.DOCUMENT_ID_1);
313 } catch (PropertyException pe) {
314 // Per CSPACE-4468, ignore any property exception here.
315 // (See parallel comment below in getObjectDocModel.)
317 if (Tools.notBlank(subjectCsid)) {
318 // FIXME: Call a utility routine here that uses the CSID to retrieve the subject record's docModel.
319 // The following is a placeholder:
320 subjectDocModel = getDocModelFromCsid(subjectCsid);
322 if (Tools.isBlank(subjectCsid)) {
324 subjectRefName = (String) relationDocModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.SUBJECT_REFNAME);
325 // FIXME: Call a utility routine here - for which the method below is currently a
326 // placeholder - that uses the refName to retrieve the subject record's docModel.
327 subjectDocModel = getDocModelFromRefname(subjectRefName);
328 } catch (Exception e) {
329 throw new InvalidDocumentException(
330 "Relation record must contain a CSID or refName to identify the subject of the relation.", e);
333 return subjectDocModel;
337 * Obtains the object resource and uses its values to populate
338 * object-related fields in the relation resource.
340 private DocumentModel getObjectDocModel(DocumentModel relationDocModel, ServiceContext ctx) throws Exception {
341 // Get the document model for the object of the relation.
342 String objectCsid = "";
343 String objectRefName = "";
344 DocumentModel objectDocModel = null;
345 // FIXME: Currently assumes that the object CSID is valid if present
346 // in the incoming payload.
348 objectCsid = (String) relationDocModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.OBJECT_CSID);
349 // FIXME: Remove this entire 'if' statement when legacy fields are removed from the Relation record:
350 if (Tools.isBlank(objectCsid)) {
351 objectCsid = (String) relationDocModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.DOCUMENT_ID_2);
353 } catch (PropertyException pe) {
354 // Per CSPACE-4468, ignore any property exception here.
355 // The objectCsid and/or subjectCsid field in a relation record
356 // can now be null (missing), because a refName value can be
357 // provided as an alternate identifier.
359 if (Tools.isBlank(objectCsid)) {
361 objectRefName = (String) relationDocModel.getProperty(ctx.getCommonPartLabel(), RelationJAXBSchema.OBJECT_REFNAME);
362 // FIXME: Call a utility routine here - for which the method below is currently a
363 // placeholder - that uses the refName to retrieve the object record's docModel.
364 objectDocModel = getDocModelFromRefname(objectRefName);
365 } catch (Exception e) {
366 throw new InvalidDocumentException(
367 "Relation record must have a CSID or refName to identify the object of the relation.", e);
370 return objectDocModel;
373 // CSPACE-4468 placeholder methods:
375 // FIXME: Placeholder method.
376 private DocumentModel getDocModelFromCsid(String csid) {
380 // FIXME: Placeholder method.
381 // Patrick is providing a working replacement for this method, in a framework class.
382 private DocumentModel getDocModelFromRefname(String csid) {
386 // FIXME: Placeholder method.
387 private DocumentModel populateSubjectValues(DocumentModel relationDocModel, DocumentModel objectDocModel) {
388 throw new UnsupportedOperationException("Not yet implemented");
391 // FIXME: Placeholder method.
392 private DocumentModel populateObjectValues(DocumentModel relationDocModel, DocumentModel objectDocModel) {
393 throw new UnsupportedOperationException("Not yet implemented");