]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
1dd57f0a2022fa9014c23e9a1b2ab303364f3bd2
[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.common.vocabulary.nuxeo;
25
26 import org.collectionspace.services.client.AuthorityClient;
27 import org.collectionspace.services.client.PayloadInputPart;
28 import org.collectionspace.services.client.PayloadOutputPart;
29 import org.collectionspace.services.client.PoxPayloadIn;
30 import org.collectionspace.services.client.PoxPayloadOut;
31 import org.collectionspace.services.client.RelationClient;
32 import org.collectionspace.services.common.api.CommonAPI;
33 import org.collectionspace.services.common.api.RefName;
34 import org.collectionspace.services.common.api.Tools;
35 import org.collectionspace.services.common.context.MultipartServiceContext;
36 import org.collectionspace.services.common.context.ServiceContext;
37 import org.collectionspace.services.common.document.DocumentWrapper;
38 import org.collectionspace.services.common.document.DocumentWrapperImpl;
39 import org.collectionspace.services.common.relation.IRelationsManager;
40 import org.collectionspace.services.common.repository.RepositoryClient;
41 import org.collectionspace.services.common.repository.RepositoryClientFactory;
42 import org.collectionspace.services.common.service.ObjectPartType;
43 import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
44 import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
45 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
46 import org.collectionspace.services.relation.RelationResource;
47 import org.collectionspace.services.relation.RelationsCommon;
48 import org.collectionspace.services.relation.RelationsCommonList;
49 import org.collectionspace.services.relation.RelationsDocListItem;
50 import org.collectionspace.services.relation.RelationshipType;
51 import org.nuxeo.ecm.core.api.DocumentModel;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 import javax.ws.rs.core.MultivaluedMap;
56 import javax.ws.rs.core.UriInfo;
57 import java.util.ArrayList;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.regex.Matcher;
61 import java.util.regex.Pattern;
62
63 //import org.collectionspace.services.common.authority.AuthorityItemRelations;
64
65 /**
66  * AuthorityItemDocumentModelHandler
67  *
68  * $LastChangedRevision: $
69  * $LastChangedDate: $
70  */
71 public abstract class AuthorityItemDocumentModelHandler<AICommon, AICommonList>
72         extends RemoteDocumentModelHandlerImpl<AICommon, AICommonList> {
73
74     private final Logger logger = LoggerFactory.getLogger(AuthorityItemDocumentModelHandler.class);
75
76         private String authorityItemCommonSchemaName;
77         
78     //private final Logger logger = LoggerFactory.getLogger(AuthorityItemDocumentModelHandler.class);
79     /**
80      * item is used to stash JAXB object to use when handle is called
81      * for Action.CREATE, Action.UPDATE or Action.GET
82      */
83     protected AICommon item;
84     /**
85      * itemList is stashed when handle is called
86      * for ACTION.GET_ALL
87      */
88     protected AICommonList itemList;
89     
90     /**
91      * inVocabulary is the parent Authority for this context
92      */
93     protected String inAuthority;
94     protected String authorityRefNameBase;
95     
96     public AuthorityItemDocumentModelHandler(String authorityItemCommonSchemaName) {
97         this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
98     }
99
100     public String getInAuthority() {
101                 return inAuthority;
102         }
103
104         public void setInAuthority(String inAuthority) {
105                 this.inAuthority = inAuthority;
106         }
107
108     /** Subclasses may override this to customize the URI segment. */
109     public String getAuthorityServicePath(){
110         return getServiceContext().getServiceName().toLowerCase();    // Laramie20110510 CSPACE-3932
111     }
112
113     @Override
114     public String getUri(DocumentModel docModel) {
115         // Laramie20110510 CSPACE-3932
116         String authorityServicePath = getAuthorityServicePath();
117         return "/"+authorityServicePath+'/'+inAuthority+'/'+ AuthorityClient.ITEMS+'/'+getCsid(docModel);
118     }
119
120     public String getAuthorityRefNameBase(){
121         return this.authorityRefNameBase;
122     }
123
124     public void setAuthorityRefNameBase(String value){
125         this.authorityRefNameBase = value;
126     }
127
128     /* (non-Javadoc)
129      * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#handleCreate(org.collectionspace.services.common.document.DocumentWrapper)
130      */
131     @Override
132     public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
133         // first fill all the parts of the document
134         super.handleCreate(wrapDoc);            
135         handleInAuthority(wrapDoc.getWrappedObject());
136         // Uncomment once debugged and App layer is read to integrate
137         //handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityItemCommonSchemaName);
138         //updateRefnameForAuthorityItem(wrapDoc, authorityItemCommonSchemaName, getAuthorityRefNameBase());  //CSPACE-3178
139     }
140     
141     private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
142         String shortIdentifier = (String)docModel.getProperty(schemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
143         String displayName =     (String)docModel.getProperty(schemaName, AuthorityItemJAXBSchema.DISPLAY_NAME);
144         if (Tools.isEmpty(shortIdentifier) && Tools.notEmpty(displayName)){
145             String cookedShortIdentifier = Tools.squeeze(displayName)+'-'+Tools.now().toString();
146             docModel.setProperty(schemaName , AuthorityItemJAXBSchema.SHORT_IDENTIFIER, cookedShortIdentifier);
147         }
148     }
149
150     protected void updateRefnameForAuthorityItem(DocumentWrapper<DocumentModel> wrapDoc,
151             String schemaName,
152             String authorityRefBaseName) throws Exception {
153         DocumentModel docModel = wrapDoc.getWrappedObject();
154         String shortIdentifier = (String)docModel.getProperty(schemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
155         String displayName =     (String)docModel.getProperty(schemaName, AuthorityItemJAXBSchema.DISPLAY_NAME);
156         if (Tools.isEmpty(authorityRefBaseName)){
157                 throw new Exception("updateRefnameForAuthorityItem requires an authorityRefBaseName, but none was supplied.");
158         }
159         RefName.Authority authority = RefName.Authority.parse(authorityRefBaseName);
160         String refName = RefName.buildAuthorityItem(authority, shortIdentifier, displayName).toString();
161         docModel.setProperty(schemaName , AuthorityItemJAXBSchema.REF_NAME, refName);
162     }
163
164     /**
165      * Check the logic around the parent pointer. Note that we only need do this on
166      * create, since we have logic to make this read-only on update. 
167      * 
168      * @param docModel
169      * 
170      * @throws Exception the exception
171      */
172     private void handleInAuthority(DocumentModel docModel) throws Exception {
173         docModel.setProperty(authorityItemCommonSchemaName, 
174                         AuthorityItemJAXBSchema.IN_AUTHORITY, inAuthority);
175     }
176
177
178     /**
179      * getCommonPart get associated item
180      * @return
181      */
182     @Override
183     public AICommon getCommonPart() {
184         return item;
185     }
186
187     @Override
188     public void setCommonPart(AICommon item) {
189         this.item = item;
190     }
191
192     /**
193      * getCommonPartList get associated item (for index/GET_ALL)
194      * @return
195      */
196     @Override
197     public AICommonList getCommonPartList() {
198         return itemList;
199     }
200
201     @Override
202     public void setCommonPartList(AICommonList itemList) {
203         this.itemList = itemList;
204     }
205
206     @Override
207     public AICommon extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc)
208             throws Exception {
209         throw new UnsupportedOperationException();
210     }
211
212     @Override
213     public void fillCommonPart(AICommon itemObject, DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
214         throw new UnsupportedOperationException();
215     }
216     
217     /* (non-Javadoc)
218      * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
219      */
220     @Override
221     protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
222             throws Exception {
223         Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
224         
225         // Add the CSID to the common part
226         if (partMeta.getLabel().equalsIgnoreCase(authorityItemCommonSchemaName)) {
227                 String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
228                 unQObjectProperties.put("csid", csid);
229         }
230         
231         return unQObjectProperties;
232     }
233     
234     /**
235      * Filters out AuthorityItemJAXBSchema.IN_AUTHORITY, to ensure that
236      * the parent link remains untouched.
237      * @param objectProps the properties parsed from the update payload
238      * @param partMeta metadata for the object to fill
239      */
240     @Override
241     public void filterReadOnlyPropertiesForPart(
242                 Map<String, Object> objectProps, ObjectPartType partMeta) {
243         super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
244         objectProps.remove(AuthorityItemJAXBSchema.IN_AUTHORITY);
245         objectProps.remove(AuthorityItemJAXBSchema.CSID);
246     }
247
248     @Override
249     public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
250         MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
251         super.extractAllParts(wrapDoc);
252
253         String showSiblings = ctx.getQueryParams().getFirst(CommonAPI.showSiblings_QP);
254         if (Tools.isTrue(showSiblings)) {
255             showSiblings(wrapDoc, ctx);
256             return;   // actual result is returned on ctx.addOutputPart();
257         }
258
259         String showRelations = ctx.getQueryParams().getFirst(CommonAPI.showRelations_QP);
260         if (Tools.isTrue(showRelations)) {
261             showRelations(wrapDoc, ctx);
262             return;   // actual result is returned on ctx.addOutputPart();
263         }
264
265         String showAllRelations = ctx.getQueryParams().getFirst(CommonAPI.showAllRelations_QP);
266         if (Tools.isTrue(showAllRelations)) {
267             showAllRelations(wrapDoc, ctx);
268             return;   // actual result is returned on ctx.addOutputPart();
269         }
270     }
271
272     /** @return null on parent not found
273      */
274     protected String getParentCSID(String thisCSID) throws Exception {
275         String parentCSID = null;
276         try {
277             String predicate = RelationshipType.HAS_BROADER.value();
278             RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
279             List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
280             if (parentList != null) {
281                 RelationsCommonList.RelationListItem relationListItem = parentList.get(0);
282                 parentCSID = relationListItem.getObjectCsid();
283             }
284             return parentCSID;
285         } catch (Exception e) {
286             logger.error("Could not find parent for this: "+thisCSID, e);
287             return null;
288         }
289     }
290
291     public void showRelations(DocumentWrapper<DocumentModel> wrapDoc,
292                                               MultipartServiceContext ctx)   throws Exception {
293         String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
294
295          String predicate = RelationshipType.HAS_BROADER.value();
296          RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
297          List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
298
299          RelationsCommonList childrenListOuter = getRelations(null, thisCSID, predicate);
300          List<RelationsCommonList.RelationListItem> childrenList = childrenListOuter.getRelationListItem();
301
302          //Assume that there are more children than parents.  Will be true for parent/child, but maybe not for other relations.
303          //Now add all parents to our childrenList, to be able to return just one list of consolidated results.
304          //Not optimal, but that's the current design spec.
305         long added = 0;
306         for (RelationsCommonList.RelationListItem parent : parentList) {
307              childrenList.add(parent);
308              added++;
309         }
310         long childrenSize = childrenList.size();
311         childrenListOuter.setTotalItems(childrenSize);
312         childrenListOuter.setItemsInPage(childrenListOuter.getItemsInPage()+added);
313
314         PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, childrenListOuter);
315         ctx.addOutputPart(relationsPart);
316     }
317
318     public void showSiblings(DocumentWrapper<DocumentModel> wrapDoc,
319                                               MultipartServiceContext ctx)   throws Exception {
320         String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
321          String parentCSID = getParentCSID(thisCSID);
322         if (parentCSID == null){
323             logger.warn("~~~~~\r\n~~~~ Could not find parent for this: "+thisCSID);
324             return;
325         }
326
327          String predicate = RelationshipType.HAS_BROADER.value();
328          RelationsCommonList siblingListOuter = getRelations(null, parentCSID, predicate);
329          List<RelationsCommonList.RelationListItem> siblingList = siblingListOuter.getRelationListItem();
330
331         List<RelationsCommonList.RelationListItem> toRemoveList = newList();
332
333
334         RelationsCommonList.RelationListItem item = null;
335         for (RelationsCommonList.RelationListItem sibling : siblingList) {
336             if (thisCSID.equals(sibling.getSubjectCsid())){
337                 toRemoveList.add(sibling);   //IS_A copy of the main item, i.e. I have a parent that is my parent, so I'm in the list from the above query.
338             }
339         }
340         //rather than create an immutable iterator, I'm just putting the items to remove on a separate list, then looping over that list and removing.
341         for (RelationsCommonList.RelationListItem self : toRemoveList) {
342             removeFromList(siblingList, self);
343         }
344
345         long siblingSize = siblingList.size();
346         siblingListOuter.setTotalItems(siblingSize);
347         siblingListOuter.setItemsInPage(siblingSize);
348
349         PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME,siblingListOuter);
350         ctx.addOutputPart(relationsPart);
351     }
352
353     public void showAllRelations(DocumentWrapper<DocumentModel> wrapDoc, MultipartServiceContext ctx)   throws Exception {
354         String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
355
356         RelationsCommonList subjectListOuter = getRelations(thisCSID, null, null);   //  nulls are wildcards:  predicate=*, and object=*
357         List<RelationsCommonList.RelationListItem> subjectList = subjectListOuter.getRelationListItem();
358
359         RelationsCommonList objectListOuter = getRelations(null, thisCSID, null);   //  nulls are wildcards:  subject=*, and predicate=*
360         List<RelationsCommonList.RelationListItem> objectList = objectListOuter.getRelationListItem();
361
362         //  MERGE LISTS:
363         subjectList.addAll(objectList);
364
365         //now subjectList actually has records BOTH where thisCSID is subject and object.
366         long relatedSize = subjectList.size();
367         subjectListOuter.setTotalItems(relatedSize);
368         subjectListOuter.setItemsInPage(relatedSize);
369
370         PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME,subjectListOuter);
371         ctx.addOutputPart(relationsPart);
372     }
373
374     public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
375         super.fillAllParts(wrapDoc, action);
376         ServiceContext ctx = getServiceContext();
377         PoxPayloadIn input = (PoxPayloadIn)ctx.getInput();
378         DocumentModel documentModel = (wrapDoc.getWrappedObject());
379         String itemCsid = documentModel.getName();
380
381         //UPDATE and CREATE will call.   Updates relations part
382         RelationsCommonList relationsCommonList = updateRelations(itemCsid, input, wrapDoc);
383
384         PayloadOutputPart payloadOutputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, relationsCommonList);
385         ctx.setProperty(RelationClient.SERVICE_COMMON_LIST_NAME, payloadOutputPart);
386     }
387
388     public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
389         super.completeUpdate(wrapDoc);
390         //now we add part for relations list
391         ServiceContext ctx = getServiceContext();
392         PayloadOutputPart foo = (PayloadOutputPart)ctx.getProperty(RelationClient.SERVICE_COMMON_LIST_NAME);
393         ((PoxPayloadOut)ctx.getOutput()).addPart(foo);
394     }
395
396      /**  updateRelations strategy:
397
398             go through inboundList, remove anything from childList that matches  from childList
399             go through inboundList, remove anything from parentList that matches  from parentList
400             go through parentList, delete all remaining
401             go through childList, delete all remaining
402             go through actionList, add all remaining.
403             check for duplicate children
404             check for more than one parent.
405
406         inboundList                           parentList                      childList          actionList
407         ----------------                          ---------------                  ----------------       ----------------
408         child-a                                   parent-c                        child-a             child-b
409         child-b                                   parent-d                        child-c
410         parent-a
411       */
412     public RelationsCommonList updateRelations(String itemCSID, PoxPayloadIn input, DocumentWrapper<DocumentModel> wrapDoc)
413      throws Exception {
414         PayloadInputPart part = input.getPart(RelationClient.SERVICE_COMMON_LIST_NAME);        //input.getPart("relations_common");
415         if (part == null) {
416             return null;  //nothing to do--they didn't send a list of relations.
417         }
418         RelationsCommonList relationsCommonListBody = (RelationsCommonList) part.getBody();
419
420         ServiceContext ctx = getServiceContext();
421         UriInfo uriInfo = ctx.getUriInfo();
422         MultivaluedMap queryParams = uriInfo.getQueryParameters();
423
424         //Run getList() once as sent to get childListOuter:
425         String predicate = RelationshipType.HAS_BROADER.value();
426         queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
427         queryParams.putSingle(IRelationsManager.SUBJECT_QP, null);
428         queryParams.putSingle(IRelationsManager.SUBJECT_TYPE_QP, null);
429         queryParams.putSingle(IRelationsManager.OBJECT_QP, itemCSID);
430         queryParams.putSingle(IRelationsManager.OBJECT_TYPE_QP, null);
431         RelationsCommonList childListOuter = (new RelationResource()).getList(ctx.getUriInfo());    //magically knows all query params because they are in the context.
432
433         //Now run getList() again, leaving predicate, swapping subject and object, to get parentListOuter.
434         queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
435         queryParams.putSingle(IRelationsManager.SUBJECT_QP, itemCSID);
436         queryParams.putSingle(IRelationsManager.OBJECT_QP, null);
437         RelationsCommonList parentListOuter = (new RelationResource()).getList(ctx.getUriInfo());
438
439         String HAS_BROADER = RelationshipType.HAS_BROADER.value();
440
441         List<RelationsCommonList.RelationListItem> inboundList = relationsCommonListBody.getRelationListItem();
442         List<RelationsCommonList.RelationListItem> actionList = newList();
443         List<RelationsCommonList.RelationListItem> childList = childListOuter.getRelationListItem();
444         List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
445
446         DocumentModel docModel = wrapDoc.getWrappedObject();
447
448         //Do magic replacement of ${itemCSID} and fix URI's.
449         fixupInboundListItems(ctx, inboundList, docModel, itemCSID);
450
451         for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
452             if (inboundItem.getObject().getCsid().equals(itemCSID) && inboundItem.getPredicate().equals(HAS_BROADER)) {
453                 //then this is an item that says we have a child.
454                 RelationsCommonList.RelationListItem childItem = findInList(childList, inboundItem);
455                 if (childItem != null){
456                     removeFromList(childList,  childItem);    //exists, just take it off delete list
457                 } else {
458                     actionList.add(inboundItem);   //doesn't exist as a child, but is a child.  Add to additions list
459                 }
460             } else if  (inboundItem.getSubject().getCsid().equals(itemCSID) && inboundItem.getPredicate().equals(HAS_BROADER)) {
461                 //then this is an item that says we have a parent
462                 RelationsCommonList.RelationListItem parentItem = findInList(parentList, inboundItem);
463                 if (parentItem != null){
464                     removeFromList(parentList,  parentItem);    //exists, just take it off delete list
465                 } else {
466                     actionList.add(inboundItem);   //doesn't exist as a parent, but is a parent. Add to additions list
467                 }
468             }  else {
469                 logger.warn("Element didn't match parent or child, but may have partial fields that match. inboundItem: "+inboundItem);
470                 //not dealing with: hasNarrower or any other predicate.
471             }
472         }
473         deleteRelations(parentList, ctx);               //todo: there are items appearing on both lists....april 20.
474         deleteRelations(childList, ctx);
475         createRelations(actionList, ctx);
476         //We return all elements on the inbound list, since we have just worked to make them exist in the system
477         // and be non-redundant, etc.  That list came from relationsCommonListBody, so it is still attached to it, just pass that back.
478         return relationsCommonListBody;
479     }
480
481     /** Performs substitution for ${itemCSID} (see CommonAPI.AuthorityItemCSID_REPLACE for constant)
482      *   and sets URI correctly for related items.
483      *   Operates directly on the items in the list.  Does not change the list ordering, does not add or remove any items.
484      */
485     protected void fixupInboundListItems(ServiceContext ctx,
486                                                             List<RelationsCommonList.RelationListItem> inboundList,
487                                                             DocumentModel docModel,
488                                                             String itemCSID) throws Exception {
489         String thisURI = this.getUri(docModel);
490         // WARNING:  the two code blocks below are almost identical  and seem to ask to be put in a generic method.
491         //                    beware of the little diffs in  inboundItem.setObjectCsid(itemCSID); and   inboundItem.setSubjectCsid(itemCSID); in the two blocks.
492         for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
493             RelationsDocListItem inboundItemObject = inboundItem.getObject();
494             RelationsDocListItem inboundItemSubject = inboundItem.getSubject();
495
496             if (inboundItemObject.getCsid().equalsIgnoreCase(CommonAPI.AuthorityItemCSID_REPLACE)){
497                 inboundItem.setObjectCsid(itemCSID);
498                 inboundItemObject.setCsid(itemCSID);
499                 inboundItemObject.setUri(getUri(docModel));
500             } else {
501                 String objectCsid = inboundItemObject.getCsid();
502                 DocumentModel itemDocModel =  NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, objectCsid);    //null if not found.
503                 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
504                 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
505                 inboundItemObject.setUri(uri);    //CSPACE-4037
506             }
507             uriPointsToSameAuthority(thisURI, inboundItemObject.getUri());    //CSPACE-4042
508
509             if (inboundItemSubject.getCsid().equalsIgnoreCase(CommonAPI.AuthorityItemCSID_REPLACE)){
510                 inboundItem.setSubjectCsid(itemCSID);
511                 inboundItemSubject.setCsid(itemCSID);
512                 inboundItemSubject.setUri(getUri(docModel));
513             } else {
514                 String subjectCsid =inboundItemSubject.getCsid();
515                 DocumentModel itemDocModel =  NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, subjectCsid);    //null if not found.
516                 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
517                 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
518                 inboundItemSubject.setUri(uri);    //CSPACE-4037
519             }
520             uriPointsToSameAuthority(thisURI, inboundItemSubject.getUri());  //CSPACE-4042
521
522         }
523     }
524
525     public RepositoryClient getRepositoryClient(ServiceContext ctx) {
526         RepositoryClient repositoryClient = RepositoryClientFactory.getInstance().getClient(ctx.getRepositoryClientName());
527         return repositoryClient;
528     }
529
530     // this method calls the RelationResource to have it create the relations and persist them.
531     private void createRelations(List<RelationsCommonList.RelationListItem> inboundList, ServiceContext ctx){
532          for (RelationsCommonList.RelationListItem item : inboundList) {
533              RelationsCommon rc = new RelationsCommon();
534              //rc.setCsid(item.getCsid());
535              //todo: assignTo(item, rc);
536              RelationsDocListItem itemSubject = item.getSubject();
537              RelationsDocListItem itemObject = item.getObject();
538
539              String subjectCsid =  itemSubject.getCsid();
540              rc.setDocumentId1(subjectCsid);
541              rc.setSubjectCsid(subjectCsid);
542
543              String objCsid = item.getObject().getCsid();
544              rc.setDocumentId2(objCsid);
545              rc.setObjectCsid(objCsid);
546
547              rc.setRelationshipType(item.getPredicate());
548              //RelationshipType  foo = (RelationshipType.valueOf(item.getPredicate())) ;
549              //rc.setPredicate(foo);     //this must be one of the type found in the enum in  services/jaxb/src/main/resources/relations_common.xsd
550
551              rc.setDocumentType1(itemSubject.getDocumentType());
552              rc.setDocumentType2(itemObject.getDocumentType());
553
554              rc.setSubjectUri(itemSubject.getUri());
555              rc.setObjectUri(itemObject.getUri());
556
557
558             PoxPayloadOut payloadOut = new PoxPayloadOut(RelationClient.SERVICE_PAYLOAD_NAME);
559             PayloadOutputPart outputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMONPART_NAME, rc);
560             payloadOut.addPart(outputPart);
561             //System.out.println("\r\n==== TO CREATE: "+rc.getDocumentId1()+"==>"+rc.getPredicate()+"==>"+rc.getDocumentId2());
562             RelationResource relationResource = new RelationResource();
563             Object res = relationResource.create(ctx.getUriInfo(), payloadOut.toXML());    //NOTE ui recycled from above to pass in unknown query params.
564         }
565     }
566      private void deleteRelations(List<RelationsCommonList.RelationListItem> list,ServiceContext ctx){
567           try {
568               for (RelationsCommonList.RelationListItem inboundItem : list) {
569                   RelationResource relationResource = new RelationResource();
570                   //System.out.println("\r\n==== TO DELETE: "+inboundItem.getCsid());
571                   Object res = relationResource.delete(inboundItem.getCsid());
572               }
573           } catch (Throwable t){
574               String msg = "Unable to deleteRelations: "+ Tools.errorToString(t, true);
575               logger.error(msg);
576           }
577      }
578
579     private  List<RelationsCommonList.RelationListItem> newList(){
580         List<RelationsCommonList.RelationListItem> result = new ArrayList<RelationsCommonList.RelationListItem>();
581         return result;
582     }
583      protected List<RelationsCommonList.RelationListItem> cloneList(List<RelationsCommonList.RelationListItem> inboundList){
584         List<RelationsCommonList.RelationListItem> result = newList();
585         for (RelationsCommonList.RelationListItem item: inboundList){
586             result.add(item);
587         }
588         return result;
589     }
590      private RelationsCommonList.RelationListItem findInList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item){
591          for (RelationsCommonList.RelationListItem listItem : list) {
592              if (itemsEqual(listItem, item)){   //equals must be defined, else
593                 return listItem;
594              }
595          }
596          return null;
597      }
598
599     private boolean itemsEqual(RelationsCommonList.RelationListItem item, RelationsCommonList.RelationListItem item2){
600         if (item==null || item2==null){
601             return false;
602         }
603         RelationsDocListItem subj1 = item.getSubject();
604         RelationsDocListItem subj2 = item2.getSubject();
605         RelationsDocListItem obj1 = item.getObject();
606         RelationsDocListItem obj2 = item2.getObject();
607
608         return     (subj1.getCsid().equals(subj2.getCsid()))
609                 && (obj1.getCsid().equals(obj1.getCsid()))
610                 && ( (item.getPredicate().equals(item2.getPredicate()))
611                 && (item.getRelationshipType().equals(item2.getRelationshipType()))   )
612                 && (obj1.getDocumentType().equals(obj2.getDocumentType()))
613                 && (subj1.getDocumentType().equals(subj2.getDocumentType())) ;
614     }
615
616      private void removeFromList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item){
617         list.remove(item);
618     }
619
620     /* don't even THINK of re-using this method.
621      * String example_uri = "/locationauthorities/7ec60f01-84ab-4908-9a6a/items/a5466530-713f-43b4-bc05";
622      */
623     private String extractInAuthorityCSID(String uri){
624         String IN_AUTHORITY_REGEX      = "/(.*?)/(.*?)/(.*)";
625         Pattern p = Pattern.compile(IN_AUTHORITY_REGEX);
626         Matcher m = p.matcher(uri);
627         if (m.find()){
628             if (m.groupCount()<3){
629                 logger.warn("REGEX-WRONG-GROUPCOUNT looking in "+uri);
630                 return "";
631             } else {
632                 //String service = m.group(1);
633                 String inauth = m.group(2);
634                 //String theRest = m.group(3);
635                 return inauth;
636                 //print("service:"+service+", inauth:"+inauth+", rest:"+rest);
637             }
638         } else {
639             logger.warn("REGEX-NOT-MATCHED looking in "+uri);
640             return "";
641         }
642     }
643
644     //ensures CSPACE-4042
645     protected void uriPointsToSameAuthority(String thisURI, String inboundItemURI) throws Exception {
646         String authorityCSID = extractInAuthorityCSID(thisURI);
647         String authorityCSIDForInbound = extractInAuthorityCSID(inboundItemURI);
648         if  (      Tools.isBlank(authorityCSID)
649                 || Tools.isBlank(authorityCSIDForInbound)
650                 ||   ( ! authorityCSID.equalsIgnoreCase(authorityCSIDForInbound) )
651             )  {
652                 throw new Exception("Item URI "+thisURI+" must point to same authority as related item: "+inboundItemURI);
653             }
654     }
655
656     //================= TODO: move this to common, refactoring this and  CollectionObjectResource.java
657     public RelationsCommonList getRelations(String subjectCSID, String objectCSID, String predicate) throws Exception {
658         ServiceContext ctx = getServiceContext();
659         MultivaluedMap queryParams = ctx.getQueryParams();
660         queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
661         queryParams.putSingle(IRelationsManager.SUBJECT_QP, subjectCSID);
662         queryParams.putSingle(IRelationsManager.OBJECT_QP, objectCSID);
663
664         RelationResource relationResource = new RelationResource();
665         RelationsCommonList relationsCommonList = relationResource.getList(ctx.getUriInfo());
666         return relationsCommonList;
667     }
668     //============================= END TODO refactor ==========================
669
670 }
671