]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
d5b757470264d711e49c112708c5ed32c6795f7d
[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 import java.net.HttpURLConnection;
29
30 import org.collectionspace.services.client.PoxPayloadIn;
31 import org.collectionspace.services.client.PoxPayloadOut;
32 import org.collectionspace.services.client.RelationClient;
33 import org.collectionspace.services.common.NuxeoBasedResource;
34 import org.collectionspace.services.common.ServiceException;
35 import org.collectionspace.services.common.ServiceMain;
36 import org.collectionspace.services.common.api.Tools;
37 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
38 import org.collectionspace.services.common.context.ServiceBindingUtils;
39 import org.collectionspace.services.common.document.DocumentNotFoundException;
40 import org.collectionspace.services.common.document.DocumentWrapper;
41 import org.collectionspace.services.common.document.InvalidDocumentException;
42 import org.collectionspace.services.common.relation.RelationJAXBSchema;
43 import org.collectionspace.services.common.relation.nuxeo.RelationConstants;
44 import org.collectionspace.services.common.context.ServiceContext;
45 import org.collectionspace.services.lifecycle.TransitionDef;
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 import org.collectionspace.services.relation.RelationsDocListItem;
51 import org.collectionspace.services.client.CollectionSpaceClient;
52 import org.collectionspace.services.client.workflow.WorkflowClient;
53 import org.collectionspace.services.config.service.ServiceBindingType;
54 import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
55 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
56 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
57
58 import org.nuxeo.ecm.core.api.ClientException;
59 import org.nuxeo.ecm.core.api.DocumentModel;
60 import org.nuxeo.ecm.core.api.DocumentModelList;
61 import org.nuxeo.ecm.core.api.model.PropertyException;
62
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 /**
67  * RelationDocumentModelHandler
68  *
69  * $LastChangedRevision: $
70  * $LastChangedDate: $
71  */
72 public class RelationDocumentModelHandler
73         extends RemoteDocumentModelHandlerImpl<RelationsCommon, RelationsCommonList> {
74
75     private final Logger logger = LoggerFactory.getLogger(RelationDocumentModelHandler.class);
76     /**
77      * relation is used to stash JAXB object to use when handle is called
78      * for Action.CREATE, Action.UPDATE or Action.GET
79      */
80     private RelationsCommon relation;
81     /**
82      * relationList is stashed when handle is called
83      * for ACTION.GET_ALL
84      */
85     private RelationsCommonList relationList;
86     
87     private static final String ERROR_TERMS_IN_WORKFLOWSTATE = "Cannot modify a relationship if either end is in the workflow state: ";
88
89     /*
90      * Will return 'true' if either the subject's or object's current workflow state *contain* the passed in workflow
91      * state.
92      * 
93      * For example:
94      *  - will return 'true' if the subject's workflow state is "replicated_deleted" and the passed in workflow state is "replicated" or "deleted".
95      *  - will return 'true' if the subject's or object's workflow state is "locked" and the passed in workflow state is "locked"
96      */
97     private boolean subjectOrObjectInWorkflowState(DocumentWrapper<DocumentModel> wrapDoc, String workflowState) throws ServiceException {
98         boolean result = false;
99         DocumentModel relationDocModel = wrapDoc.getWrappedObject();
100         String errMsg = ERROR_TERMS_IN_WORKFLOWSTATE + workflowState;
101                         
102         CoreSessionInterface repoSession = this.getRepositorySession();
103         try {
104                         DocumentModel subjectDocModel = getSubjectOrObjectDocModel(repoSession, relationDocModel, SUBJ_DOC_MODEL);
105                         DocumentModel objectDocModel = getSubjectOrObjectDocModel(repoSession, relationDocModel, OBJ_DOC_MODEL);
106                         if (subjectDocModel.getCurrentLifeCycleState().contains(workflowState) ||
107                                         objectDocModel.getCurrentLifeCycleState().contains(workflowState)) {
108                                 result = true;
109                         }
110                 } catch (Exception e) {
111                         if (logger.isInfoEnabled() == true) {
112                                 logger.info(errMsg, e);
113                         }
114                 }
115                 
116         return result;
117     }
118     
119         @Override
120         /*
121          * Until we rework the RepositoryClient to handle the workflow transition (just like it does for 'create', 'get', 'update', and 'delete'), this method will only check to see
122          * if the transition is allowed.  Until then, the WorkflowDocumentModelHandler class does the actual workflow transition.
123          * 
124          * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#handleWorkflowTransition(org.collectionspace.services.common.document.DocumentWrapper, org.collectionspace.services.lifecycle.TransitionDef)
125          */
126         public void handleWorkflowTransition(ServiceContext ctx, DocumentWrapper<DocumentModel> wrapDoc,
127                         TransitionDef transitionDef) throws Exception {
128                 if (subjectOrObjectInWorkflowState(wrapDoc, WorkflowClient.WORKFLOWSTATE_LOCKED) == true) {
129                         throw new ServiceException(HttpURLConnection.HTTP_FORBIDDEN,
130                                         "Cannot change a relationship if either end of it is in the workflow state: "
131                                                         + WorkflowClient.WORKFLOWSTATE_LOCKED);
132                 } else {
133                         //
134                         // Toggle the 'active' flag of the relationship record -needed to correctly apply a uniqueness constrain on rows in the relations_common table
135                         //
136                         String transitionName = transitionDef.getName();
137                         if (transitionName.equalsIgnoreCase(WorkflowClient.WORKFLOWTRANSITION_UNDELETE)) {
138                                 DocumentModel doc = wrapDoc.getWrappedObject();
139                                 doc.setProperty(RelationClient.SERVICE_COMMONPART_NAME, RelationJAXBSchema.RELATIONSHIP_ACTIVE, Boolean.TRUE);
140                         } else if (transitionName.equalsIgnoreCase(WorkflowClient.WORKFLOWTRANSITION_DELETE)) {
141                                 DocumentModel doc = wrapDoc.getWrappedObject();
142                                 doc.setProperty(RelationClient.SERVICE_COMMONPART_NAME, RelationJAXBSchema.RELATIONSHIP_ACTIVE, Boolean.FALSE);
143                         }
144                 }
145                 
146         }
147
148     @Override
149     public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
150         // Merge in the data from the payload
151         super.handleCreate(wrapDoc);
152
153         // And take care of ensuring all the values for the relation info are correct 
154         populateSubjectAndObjectValues(wrapDoc);
155         
156         // We can't create a relationship record if either the subject or the object is in a locked workflow state
157         String workflowState = WorkflowClient.WORKFLOWSTATE_LOCKED;
158         if (subjectOrObjectInWorkflowState(wrapDoc, workflowState) == true) {
159                 throw new ServiceException(HttpURLConnection.HTTP_FORBIDDEN,
160                     "Cannot create a relationship if either end is in the workflow state: " + workflowState);
161         }
162     }
163
164     @Override
165     public void handleUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
166         // Merge in the data from the payload
167         super.handleUpdate(wrapDoc);
168         
169         // And take care of ensuring all the values for the relation info are correct 
170         populateSubjectAndObjectValues(wrapDoc);
171     }
172     
173     @Override
174     public boolean handleDelete(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
175         boolean result = true;
176         
177         String workflowState = WorkflowClient.WORKFLOWSTATE_LOCKED;
178         // Neither the subject nor the object can be locked
179         if (subjectOrObjectInWorkflowState(wrapDoc, workflowState) == false) {
180                 super.handleDelete(wrapDoc);
181         } else {
182                 throw new ServiceException(HttpURLConnection.HTTP_FORBIDDEN,
183                     "Cannot delete a relationship if either end is in the workflow state: " + workflowState);
184         }
185         
186         return result;
187     }
188     
189     private void populateSubjectAndObjectValues(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
190         // Obtain document models for the subject and object of the relation, so that
191         // we ensure we have value docType, URI info. If the docModels support refNames, 
192         // we will also set those.
193         // Note that this introduces another caching problem... 
194         DocumentModel relationDocModel = wrapDoc.getWrappedObject();
195         CoreSessionInterface repoSession = this.getRepositorySession();
196         
197         DocumentModel subjectDocModel = getSubjectOrObjectDocModel(repoSession, relationDocModel, SUBJ_DOC_MODEL);
198         DocumentModel objectDocModel = getSubjectOrObjectDocModel(repoSession, relationDocModel, OBJ_DOC_MODEL);
199
200         // Use values from the subject and object document models to populate the
201         // relevant fields of the relation's own document model.
202         if (subjectDocModel != null) {
203             populateSubjectOrObjectValues(relationDocModel, subjectDocModel, SUBJ_DOC_MODEL);
204         }
205         if (objectDocModel != null) {
206             populateSubjectOrObjectValues(relationDocModel, objectDocModel, OBJ_DOC_MODEL);
207         }
208     }
209
210     @Override
211     public RelationsCommon getCommonPart() {
212         return relation;
213     }
214
215     @Override
216     public void setCommonPart(RelationsCommon theRelation) {
217         this.relation = theRelation;
218     }
219
220     /**get associated Relation (for index/GET_ALL)
221      */
222     @Override
223     public RelationsCommonList getCommonPartList() {
224         return relationList;
225     }
226
227     @Override
228     public void setCommonPartList(RelationsCommonList theRelationList) {
229         this.relationList = theRelationList;
230     }
231
232     @Override
233     public RelationsCommon extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc)
234             throws Exception {
235         throw new UnsupportedOperationException();
236     }
237
238     @Override
239     public void fillCommonPart(RelationsCommon theRelation, DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
240         throw new UnsupportedOperationException();
241     }
242
243     @Override
244     public RelationsCommonList extractCommonPartList(DocumentWrapper<DocumentModelList> wrapDoc) throws Exception {
245         RelationsCommonList relList = this.extractPagingInfo(new RelationsCommonList(), wrapDoc);
246         relList.setFieldsReturned("subjectCsid|relationshipType|predicateDisplayName|relationshipMetaType|objectCsid|uri|csid|subject|object");
247         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
248         String serviceContextPath = getServiceContextPath();
249
250         TenantBindingConfigReaderImpl tReader = ServiceMain.getInstance().getTenantBindingConfigReader();
251         String serviceName = getServiceContext().getServiceName().toLowerCase();
252         ServiceBindingType sbt = tReader.getServiceBinding(ctx.getTenantId(), serviceName);
253
254         Iterator<DocumentModel> iter = wrapDoc.getWrappedObject().iterator();
255         while (iter.hasNext()) {
256             DocumentModel docModel = iter.next();
257             RelationListItem relListItem = getRelationListItem(ctx, sbt, tReader, docModel, serviceContextPath);
258             relList.getRelationListItem().add(relListItem);
259         }
260         return relList;
261     }
262
263     /** Gets the relation list item, looking up the subject and object documents, and getting summary
264      *  info via the objectName and objectNumber properties in tenant-bindings.
265      * @param ctx the ctx
266      * @param sbt the ServiceBindingType of Relations service
267      * @param tReader the tenant-bindings reader, for looking up docnumber and docname
268      * @param docModel the doc model
269      * @param serviceContextPath the service context path
270      * @return the relation list item, with nested subject and object summary info.
271      * @throws Exception the exception
272      */
273     private RelationListItem getRelationListItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
274             ServiceBindingType sbt,
275             TenantBindingConfigReaderImpl tReader,
276             DocumentModel docModel,
277             String serviceContextPath) throws Exception {
278         RelationListItem relationListItem = new RelationListItem();
279         String id = getCsid(docModel);
280         relationListItem.setCsid(id);
281
282         relationListItem.setSubjectCsid((String) docModel.getProperty(ctx.getCommonPartLabel(), 
283                                                                                                         RelationJAXBSchema.SUBJECT_CSID));
284
285         String predicate = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
286                                                                                                         RelationJAXBSchema.RELATIONSHIP_TYPE);
287         relationListItem.setRelationshipType(predicate);
288         relationListItem.setPredicate(predicate); //predicate is new name for relationshipType.
289         relationListItem.setPredicateDisplayName((String) docModel.getProperty(ctx.getCommonPartLabel(), 
290                                                                                                         RelationJAXBSchema.RELATIONSHIP_TYPE_DISPLAYNAME));
291
292         relationListItem.setRelationshipMetaType((String) docModel.getProperty(ctx.getCommonPartLabel(), 
293                                                                                                         RelationJAXBSchema.RELATIONSHIP_META_TYPE));
294         relationListItem.setObjectCsid((String) docModel.getProperty(ctx.getCommonPartLabel(), 
295                                                                                                         RelationJAXBSchema.OBJECT_CSID));
296
297         relationListItem.setUri(serviceContextPath + id);
298
299         //Now fill in summary info for the related docs: subject and object.
300         String subjectCsid = relationListItem.getSubjectCsid();
301         String subjectDocumentType = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
302                                                                                                         RelationJAXBSchema.SUBJECT_DOCTYPE);
303         RelationsDocListItem subject = createRelationsDocListItem(ctx, sbt, subjectCsid, tReader, subjectDocumentType);
304
305         String subjectUri = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
306                                                                                                         RelationJAXBSchema.SUBJECT_URI);
307         subject.setUri(subjectUri);
308         String subjectRefName = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
309                                                                                                         RelationJAXBSchema.SUBJECT_REFNAME);
310         subject.setRefName(subjectRefName);
311         relationListItem.setSubject(subject);
312
313         String objectCsid = relationListItem.getObjectCsid();
314         String objectDocumentType = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
315                                                                                                         RelationJAXBSchema.OBJECT_DOCTYPE);
316         RelationsDocListItem object = createRelationsDocListItem(ctx, sbt, objectCsid, tReader, objectDocumentType);
317
318         String objectUri = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
319                                                                                                         RelationJAXBSchema.OBJECT_URI);
320         object.setUri(objectUri);
321         String objectRefName = (String) docModel.getProperty(ctx.getCommonPartLabel(), 
322                                                                                                         RelationJAXBSchema.OBJECT_REFNAME);
323         object.setRefName(objectRefName);
324         relationListItem.setObject(object);
325
326         return relationListItem;
327     }
328
329     // DocumentModel itemDocModel = docModelFromCSID(ctx, itemCsid);
330     protected RelationsDocListItem createRelationsDocListItem(
331                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
332             ServiceBindingType sbt,
333             String itemCsid,
334             TenantBindingConfigReaderImpl tReader,
335             String documentType) throws Exception {
336         RelationsDocListItem item = new RelationsDocListItem();
337         item.setDocumentType(documentType);//this one comes from the record, as subjectDocumentType, objectDocumentType.
338         item.setCsid(itemCsid);
339
340         DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(ctx, this.getRepositorySession(), itemCsid);    //null if not found.
341         if (itemDocModel != null) {
342             String itemDocType = itemDocModel.getDocumentType().getName();
343             itemDocType = ServiceBindingUtils.getUnqualifiedTenantDocType(itemDocType);
344             if (Tools.isBlank(documentType)) {
345                 item.setDocumentType(itemDocType);
346             }
347
348             //TODO: ensure that itemDocType is really the entry point, i.e. servicename==doctype
349             //ServiceBindingType itemSbt2 = tReader.getServiceBinding(ctx.getTenantId(), itemDocType);
350             String propName = "ERROR-FINDING-PROP-VALUE";
351             ServiceBindingType itemSbt = tReader.getServiceBindingForDocType(ctx.getTenantId(), itemDocType);
352             try {
353                 propName = ServiceBindingUtils.getPropertyValue(itemSbt, ServiceBindingUtils.OBJ_NAME_PROP);
354                 String itemDocname = ServiceBindingUtils.getMappedFieldInDoc(itemSbt, ServiceBindingUtils.OBJ_NAME_PROP, itemDocModel);
355                 if (propName == null || itemDocname == null) {
356                 } else {
357                     item.setName(itemDocname);
358                 }
359             } catch (Throwable t) {
360                 logger.error("====Error finding objectNameProperty: " + itemDocModel + " field " + ServiceBindingUtils.OBJ_NAME_PROP + "=" + propName
361                         + " not found in itemDocType: " + itemDocType + " inner: " + t.getMessage());
362             }
363             propName = "ERROR-FINDING-PROP-VALUE";
364             try {
365                 propName = ServiceBindingUtils.getPropertyValue(itemSbt, ServiceBindingUtils.OBJ_NUMBER_PROP);
366                 String itemDocnumber = ServiceBindingUtils.getMappedFieldInDoc(itemSbt, ServiceBindingUtils.OBJ_NUMBER_PROP, itemDocModel);
367
368                 if (propName == null || itemDocnumber == null) {
369                 } else {
370                     item.setNumber(itemDocnumber);
371                 }
372             } catch (Throwable t) {
373                 logger.error("====Error finding objectNumberProperty: " + ServiceBindingUtils.OBJ_NUMBER_PROP + "=" + propName
374                         + " not found in itemDocType: " + itemDocType + " inner: " + t.getMessage());
375             }
376         } else {
377             item.setError("INVALID: related object is absent");
378             // Laramie20110510 CSPACE-3739  throw the exception for 3739, otherwise, don't throw it.
379             //throw new Exception("INVALID: related object is absent "+itemCsid);
380         }
381         return item;
382     }
383
384     @Override
385     public String getQProperty(String prop) {
386         return "/" + RelationConstants.NUXEO_SCHEMA_ROOT_ELEMENT + "/" + prop;
387     }
388
389     private final boolean SUBJ_DOC_MODEL = true;
390     private final boolean OBJ_DOC_MODEL = false;
391     
392     private DocumentModel getSubjectOrObjectDocModel(
393                 CoreSessionInterface repoSession,
394                 DocumentModel relationDocModel,
395                 boolean fSubject) throws Exception {
396         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
397         
398         // Get the document model for the object of the relation.
399         String commonPartLabel = ctx.getCommonPartLabel();
400         String csid = "";
401         String refName = "";
402         DocumentModel docModel = null;
403         // FIXME: Currently assumes that the object CSID is valid if present
404         // in the incoming payload.
405         try {
406             csid = (String) relationDocModel.getProperty(commonPartLabel, 
407                         (fSubject?RelationJAXBSchema.SUBJECT_CSID:RelationJAXBSchema.OBJECT_CSID));
408         } catch (PropertyException pe) {
409             // Per CSPACE-4468, ignore any property exception here.
410             // The objectCsid and/or subjectCsid field in a relation record
411             // can now be null (missing), because a refName value can be
412             // provided as an alternate identifier.
413         }
414         if (Tools.notBlank(csid)) {
415                 NuxeoRepositoryClientImpl nuxeoRepoClient = (NuxeoRepositoryClientImpl)getRepositoryClient(ctx);
416             DocumentWrapper<DocumentModel> docWrapper = nuxeoRepoClient.getDocFromCsid(ctx, repoSession, csid);
417             docModel = docWrapper.getWrappedObject();
418         } else { //  if (Tools.isBlank(objectCsid)) {
419             try {
420                 refName = (String) relationDocModel.getProperty(commonPartLabel, 
421                                 (fSubject?RelationJAXBSchema.SUBJECT_REFNAME:RelationJAXBSchema.OBJECT_REFNAME));
422                 docModel = NuxeoBasedResource.getDocModelForRefName(ctx, refName, ctx.getResourceMap());
423             } catch (Exception e) {
424                 throw new InvalidDocumentException(
425                         "Relation record must have a CSID or refName to identify the object of the relation.", e);
426             }
427         }
428         if(docModel==null) {
429                 throw new DocumentNotFoundException("RelationDMH.getSubjectOrObjectDocModel could not find doc with CSID: "
430                                         +csid+" and/or refName: "+refName );
431         }
432         return docModel;
433     }
434     
435     private void populateSubjectOrObjectValues(
436                 DocumentModel relationDocModel, 
437                 DocumentModel subjectOrObjectDocModel,
438                 boolean fSubject ) {
439         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
440         
441         HashMap<String,Object> properties = new HashMap<String,Object>();
442         try {
443                 String doctype = subjectOrObjectDocModel.getDocumentType().getName();
444             doctype = ServiceBindingUtils.getUnqualifiedTenantDocType(doctype);
445                 properties.put((fSubject?RelationJAXBSchema.SUBJECT_DOCTYPE:RelationJAXBSchema.OBJECT_DOCTYPE),
446                                                         doctype);
447         
448                 String csid = (String) subjectOrObjectDocModel.getName();
449                 properties.put((fSubject?RelationJAXBSchema.SUBJECT_CSID:RelationJAXBSchema.OBJECT_CSID),
450                                                         csid);
451         
452                 String uri = (String) subjectOrObjectDocModel.getProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
453                                 CollectionSpaceClient.COLLECTIONSPACE_CORE_URI);
454                 properties.put((fSubject?RelationJAXBSchema.SUBJECT_URI:RelationJAXBSchema.OBJECT_URI),
455                                                         uri);
456                 
457                 /*
458                 String common_schema = getCommonSchemaNameForDocType(doctype);
459                 
460                 if(common_schema!=null) {
461                         String refname = (String)subjectOrObjectDocModel.getProperty(common_schema, 
462                                                                                                                         RefName.REFNAME );
463                     properties.put((fSubject?RelationJAXBSchema.SUBJECT_REFNAME:RelationJAXBSchema.OBJECT_REFNAME),
464                                 refname);
465                 }
466                 */
467                 String refname = (String) 
468                                 subjectOrObjectDocModel.getProperty(
469                                                 CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
470                                                 CollectionSpaceClient.COLLECTIONSPACE_CORE_REFNAME);
471             properties.put((fSubject?
472                                                 RelationJAXBSchema.SUBJECT_REFNAME
473                                                 :RelationJAXBSchema.OBJECT_REFNAME),
474                                         refname);
475         } catch (ClientException ce) {
476             throw new RuntimeException(
477                     "populateSubjectOrObjectValues: Problem fetching field " + ce.getLocalizedMessage());
478         }
479
480         // FIXME: Call below is based solely on Nuxeo API docs; have not yet verified that it correctly updates existing
481         // property values in the target document model.
482         try {
483                 relationDocModel.setProperties(ctx.getCommonPartLabel(), properties);
484         } catch (ClientException ce) {
485             throw new RuntimeException(
486                     "populateSubjectValues: Problem setting fields " + ce.getLocalizedMessage());
487         }
488     }
489     
490     @Override
491         public boolean supportsWorkflowStates() {
492                 return true;
493         }
494 }