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.common.vocabulary.nuxeo;
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.AuthorityJAXBSchema;
44 import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
45 import org.collectionspace.services.common.vocabulary.AuthorityResource;
46 import org.collectionspace.services.nuxeo.client.java.DocHandlerBase;
47 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
48 import org.collectionspace.services.relation.RelationResource;
49 import org.collectionspace.services.relation.RelationsCommon;
50 import org.collectionspace.services.relation.RelationsCommonList;
51 import org.collectionspace.services.relation.RelationsDocListItem;
52 import org.collectionspace.services.relation.RelationshipType;
53 import org.nuxeo.ecm.core.api.DocumentModel;
54 import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
58 import javax.ws.rs.core.MultivaluedMap;
59 import javax.ws.rs.core.UriInfo;
60 import java.util.ArrayList;
61 import java.util.List;
63 import java.util.regex.Matcher;
64 import java.util.regex.Pattern;
66 //import org.collectionspace.services.common.authority.AuthorityItemRelations;
68 * AuthorityItemDocumentModelHandler
70 * $LastChangedRevision: $
73 public abstract class AuthorityItemDocumentModelHandler<AICommon>
74 extends DocHandlerBase<AICommon> {
76 private final Logger logger = LoggerFactory.getLogger(AuthorityItemDocumentModelHandler.class);
77 private String authorityItemCommonSchemaName;
79 * inVocabulary is the parent Authority for this context
81 protected String inAuthority;
82 protected String authorityRefNameBase;
84 public AuthorityItemDocumentModelHandler(String authorityItemCommonSchemaName) {
85 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
88 public String getInAuthority() {
92 public void setInAuthority(String inAuthority) {
93 this.inAuthority = inAuthority;
96 /** Subclasses may override this to customize the URI segment. */
97 public String getAuthorityServicePath() {
98 return getServiceContext().getServiceName().toLowerCase(); // Laramie20110510 CSPACE-3932
102 public String getUri(DocumentModel docModel) {
103 // Laramie20110510 CSPACE-3932
104 String authorityServicePath = getAuthorityServicePath();
105 return "/" + authorityServicePath + '/' + inAuthority + '/' + AuthorityClient.ITEMS + '/' + getCsid(docModel);
108 public String getAuthorityRefNameBase() {
109 return this.authorityRefNameBase;
112 public void setAuthorityRefNameBase(String value) {
113 this.authorityRefNameBase = value;
117 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#handleCreate(org.collectionspace.services.common.document.DocumentWrapper)
120 public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
121 // first fill all the parts of the document
122 super.handleCreate(wrapDoc);
123 handleInAuthority(wrapDoc.getWrappedObject());
125 // Uncomment once debugged and App layer is read to integrate
126 // Experimenting with these uncommented now ...
127 handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityItemCommonSchemaName);
128 updateRefnameForAuthorityItem(wrapDoc, authorityItemCommonSchemaName, getAuthorityRefNameBase());
131 private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
132 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
133 String displayName = (String) docModel.getProperty(schemaName, AuthorityItemJAXBSchema.DISPLAY_NAME);
134 String shortDisplayName = "";
136 shortDisplayName = (String) docModel.getProperty(schemaName, AuthorityItemJAXBSchema.SHORT_DISPLAY_NAME);
137 } catch (PropertyNotFoundException pnfe) {
138 // Do nothing on exception. Some vocabulary schemas may not include a short display name.
140 if (Tools.isEmpty(shortIdentifier)) {
141 String generatedShortIdentifier = generateShortIdentifierFromDisplayName(displayName, shortDisplayName);
142 docModel.setProperty(schemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER, generatedShortIdentifier);
147 // FIXME: Consider replacing this with a different algorithm, perhaps one
148 // that combines stems of each word token in the displayname.
149 // FIXME: Verify uniqueness before returning the generated short identifier.
150 // FIXME: Consider using a hash of the display name, rather than a timestamp,
151 // when it is necessary to add a suffix for uniqueness.
152 private String generateShortIdentifierFromDisplayName(String displayName, String shortDisplayName) {
153 String generatedShortIdentifier = "";
154 if (Tools.notEmpty(displayName)) {
155 generatedShortIdentifier = displayName + '-' + Tools.now().toString();
156 } else if (Tools.notEmpty(shortDisplayName)) {
157 generatedShortIdentifier = shortDisplayName + '-' + Tools.now().toString();
159 // Ensure that the short identifier consists only of word chars.
160 if (Tools.notEmpty(generatedShortIdentifier)) {
161 generatedShortIdentifier = generatedShortIdentifier.replaceAll("[^\\w]", "");
163 // Fallback if we can't generate a short identifier from the displayname(s).
164 if (generatedShortIdentifier.isEmpty()) {
165 generatedShortIdentifier = java.util.UUID.randomUUID().toString();
167 return generatedShortIdentifier;
170 protected void updateRefnameForAuthorityItem(DocumentWrapper<DocumentModel> wrapDoc,
172 String authorityRefBaseName) throws Exception {
173 DocumentModel docModel = wrapDoc.getWrappedObject();
174 String suppliedRefName = (String) docModel.getProperty(schemaName, AuthorityItemJAXBSchema.REF_NAME);
176 // Temporarily accept client-supplied refName values, rather than always generating such values.
177 // Remove the surrounding 'if' statement when clients should no longer supply refName values.
178 if (suppliedRefName == null || suppliedRefName.isEmpty()) {
179 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
180 String displayName = (String) docModel.getProperty(schemaName, AuthorityItemJAXBSchema.DISPLAY_NAME);
181 if (Tools.isEmpty(authorityRefBaseName)) {
182 throw new Exception("Could not create the refName for this authority term, because the refName for its authority parent was empty.");
184 RefName.Authority authority = RefName.Authority.parse(authorityRefBaseName);
185 String refName = RefName.buildAuthorityItem(authority, shortIdentifier, displayName).toString();
186 docModel.setProperty(schemaName, AuthorityItemJAXBSchema.REF_NAME, refName);
191 * Check the logic around the parent pointer. Note that we only need do this on
192 * create, since we have logic to make this read-only on update.
196 * @throws Exception the exception
198 private void handleInAuthority(DocumentModel docModel) throws Exception {
199 docModel.setProperty(authorityItemCommonSchemaName,
200 AuthorityItemJAXBSchema.IN_AUTHORITY, inAuthority);
205 * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
208 protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
210 Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
212 // Add the CSID to the common part
213 if (partMeta.getLabel().equalsIgnoreCase(authorityItemCommonSchemaName)) {
214 String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
215 unQObjectProperties.put("csid", csid);
218 return unQObjectProperties;
222 * Filters out values supplied in the request; e.g.:
223 * AuthorityItemJAXBSchema.IN_AUTHORITY, to ensure that
224 * the parent link remains untouched.
225 * @param objectProps the properties parsed from the update payload
226 * @param partMeta metadata for the object to fill
229 public void filterReadOnlyPropertiesForPart(
230 Map<String, Object> objectProps, ObjectPartType partMeta) {
231 super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
232 String commonPartLabel = getServiceContext().getCommonPartLabel();
233 if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
234 objectProps.remove(AuthorityItemJAXBSchema.IN_AUTHORITY);
235 objectProps.remove(AuthorityItemJAXBSchema.CSID);
236 // Enable when clients should no longer supply refName values
237 // objectProps.remove(AuthorityItemJAXBSchema.REF_NAME); // CSPACE-3178
243 public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
244 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
245 super.extractAllParts(wrapDoc);
247 String showSiblings = ctx.getQueryParams().getFirst(CommonAPI.showSiblings_QP);
248 if (Tools.isTrue(showSiblings)) {
249 showSiblings(wrapDoc, ctx);
250 return; // actual result is returned on ctx.addOutputPart();
253 String showRelations = ctx.getQueryParams().getFirst(CommonAPI.showRelations_QP);
254 if (Tools.isTrue(showRelations)) {
255 showRelations(wrapDoc, ctx);
256 return; // actual result is returned on ctx.addOutputPart();
259 String showAllRelations = ctx.getQueryParams().getFirst(CommonAPI.showAllRelations_QP);
260 if (Tools.isTrue(showAllRelations)) {
261 showAllRelations(wrapDoc, ctx);
262 return; // actual result is returned on ctx.addOutputPart();
266 /** @return null on parent not found
268 protected String getParentCSID(String thisCSID) throws Exception {
269 String parentCSID = null;
271 String predicate = RelationshipType.HAS_BROADER.value();
272 RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
273 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
274 if (parentList != null) {
275 if (parentList.size() == 0) {
278 RelationsCommonList.RelationListItem relationListItem = parentList.get(0);
279 parentCSID = relationListItem.getObjectCsid();
282 } catch (Exception e) {
283 logger.error("Could not find parent for this: " + thisCSID, e);
288 public void showRelations(DocumentWrapper<DocumentModel> wrapDoc,
289 MultipartServiceContext ctx) throws Exception {
290 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
292 String predicate = RelationshipType.HAS_BROADER.value();
293 RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
294 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
296 RelationsCommonList childrenListOuter = getRelations(null, thisCSID, predicate);
297 List<RelationsCommonList.RelationListItem> childrenList = childrenListOuter.getRelationListItem();
299 //Assume that there are more children than parents. Will be true for parent/child, but maybe not for other relations.
300 //Now add all parents to our childrenList, to be able to return just one list of consolidated results.
301 //Not optimal, but that's the current design spec.
303 for (RelationsCommonList.RelationListItem parent : parentList) {
304 childrenList.add(parent);
307 long childrenSize = childrenList.size();
308 childrenListOuter.setTotalItems(childrenSize);
309 childrenListOuter.setItemsInPage(childrenListOuter.getItemsInPage() + added);
311 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, childrenListOuter);
312 ctx.addOutputPart(relationsPart);
315 public void showSiblings(DocumentWrapper<DocumentModel> wrapDoc,
316 MultipartServiceContext ctx) throws Exception {
317 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
318 String parentCSID = getParentCSID(thisCSID);
319 if (parentCSID == null) {
320 logger.warn("~~~~~\r\n~~~~ Could not find parent for this: " + thisCSID);
324 String predicate = RelationshipType.HAS_BROADER.value();
325 RelationsCommonList siblingListOuter = getRelations(null, parentCSID, predicate);
326 List<RelationsCommonList.RelationListItem> siblingList = siblingListOuter.getRelationListItem();
328 List<RelationsCommonList.RelationListItem> toRemoveList = newList();
331 RelationsCommonList.RelationListItem item = null;
332 for (RelationsCommonList.RelationListItem sibling : siblingList) {
333 if (thisCSID.equals(sibling.getSubjectCsid())) {
334 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.
337 //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.
338 for (RelationsCommonList.RelationListItem self : toRemoveList) {
339 removeFromList(siblingList, self);
342 long siblingSize = siblingList.size();
343 siblingListOuter.setTotalItems(siblingSize);
344 siblingListOuter.setItemsInPage(siblingSize);
346 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, siblingListOuter);
347 ctx.addOutputPart(relationsPart);
350 public void showAllRelations(DocumentWrapper<DocumentModel> wrapDoc, MultipartServiceContext ctx) throws Exception {
351 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
353 RelationsCommonList subjectListOuter = getRelations(thisCSID, null, null); // nulls are wildcards: predicate=*, and object=*
354 List<RelationsCommonList.RelationListItem> subjectList = subjectListOuter.getRelationListItem();
356 RelationsCommonList objectListOuter = getRelations(null, thisCSID, null); // nulls are wildcards: subject=*, and predicate=*
357 List<RelationsCommonList.RelationListItem> objectList = objectListOuter.getRelationListItem();
360 subjectList.addAll(objectList);
362 //now subjectList actually has records BOTH where thisCSID is subject and object.
363 long relatedSize = subjectList.size();
364 subjectListOuter.setTotalItems(relatedSize);
365 subjectListOuter.setItemsInPage(relatedSize);
367 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, subjectListOuter);
368 ctx.addOutputPart(relationsPart);
371 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
372 super.fillAllParts(wrapDoc, action);
373 ServiceContext ctx = getServiceContext();
374 PoxPayloadIn input = (PoxPayloadIn) ctx.getInput();
375 DocumentModel documentModel = (wrapDoc.getWrappedObject());
376 String itemCsid = documentModel.getName();
378 //UPDATE and CREATE will call. Updates relations part
379 RelationsCommonList relationsCommonList = updateRelations(itemCsid, input, wrapDoc);
381 PayloadOutputPart payloadOutputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, relationsCommonList);
382 ctx.setProperty(RelationClient.SERVICE_COMMON_LIST_NAME, payloadOutputPart);
385 public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
386 super.completeUpdate(wrapDoc);
387 //now we add part for relations list
388 ServiceContext ctx = getServiceContext();
389 PayloadOutputPart foo = (PayloadOutputPart) ctx.getProperty(RelationClient.SERVICE_COMMON_LIST_NAME);
390 ((PoxPayloadOut) ctx.getOutput()).addPart(foo);
393 /** updateRelations strategy:
395 go through inboundList, remove anything from childList that matches from childList
396 go through inboundList, remove anything from parentList that matches from parentList
397 go through parentList, delete all remaining
398 go through childList, delete all remaining
399 go through actionList, add all remaining.
400 check for duplicate children
401 check for more than one parent.
403 inboundList parentList childList actionList
404 ---------------- --------------- ---------------- ----------------
405 child-a parent-c child-a child-b
406 child-b parent-d child-c
409 public RelationsCommonList updateRelations(String itemCSID, PoxPayloadIn input, DocumentWrapper<DocumentModel> wrapDoc)
411 PayloadInputPart part = input.getPart(RelationClient.SERVICE_COMMON_LIST_NAME); //input.getPart("relations_common");
413 return null; //nothing to do--they didn't send a list of relations.
415 RelationsCommonList relationsCommonListBody = (RelationsCommonList) part.getBody();
417 ServiceContext ctx = getServiceContext();
418 UriInfo uriInfo = ctx.getUriInfo();
419 MultivaluedMap queryParams = uriInfo.getQueryParameters();
421 //Run getList() once as sent to get childListOuter:
422 String predicate = RelationshipType.HAS_BROADER.value();
423 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
424 queryParams.putSingle(IRelationsManager.SUBJECT_QP, null);
425 queryParams.putSingle(IRelationsManager.SUBJECT_TYPE_QP, null);
426 queryParams.putSingle(IRelationsManager.OBJECT_QP, itemCSID);
427 queryParams.putSingle(IRelationsManager.OBJECT_TYPE_QP, null);
428 RelationsCommonList childListOuter = (new RelationResource()).getList(ctx.getUriInfo()); //magically knows all query params because they are in the context.
430 //Now run getList() again, leaving predicate, swapping subject and object, to get parentListOuter.
431 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
432 queryParams.putSingle(IRelationsManager.SUBJECT_QP, itemCSID);
433 queryParams.putSingle(IRelationsManager.OBJECT_QP, null);
434 RelationsCommonList parentListOuter = (new RelationResource()).getList(ctx.getUriInfo());
436 String HAS_BROADER = RelationshipType.HAS_BROADER.value();
438 List<RelationsCommonList.RelationListItem> inboundList = relationsCommonListBody.getRelationListItem();
439 List<RelationsCommonList.RelationListItem> actionList = newList();
440 List<RelationsCommonList.RelationListItem> childList = childListOuter.getRelationListItem();
441 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
443 if (parentList.size() > 1) {
444 throw new Exception("Too many parents for object: " + itemCSID + " list: " + dumpList(parentList, "parentList"));
447 DocumentModel docModel = wrapDoc.getWrappedObject();
449 //Do magic replacement of ${itemCSID} and fix URI's.
450 fixupInboundListItems(ctx, inboundList, docModel, itemCSID);
452 for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
453 if (inboundItem.getObject().getCsid().equals(itemCSID) && inboundItem.getPredicate().equals(HAS_BROADER)) {
454 //then this is an item that says we have a child. That child is inboundItem
455 RelationsCommonList.RelationListItem childItem = findInList(childList, inboundItem);
456 if (childItem != null) {
457 removeFromList(childList, childItem); //exists, just take it off delete list
459 actionList.add(inboundItem); //doesn't exist as a child, but is a child. Add to additions list
461 ensureChildHasNoOtherParents(ctx, queryParams, inboundItem.getSubject().getCsid());
463 } else if (inboundItem.getSubject().getCsid().equals(itemCSID) && inboundItem.getPredicate().equals(HAS_BROADER)) {
464 //then this is an item that says we have a parent. inboundItem is that parent.
465 RelationsCommonList.RelationListItem parentItem = findInList(parentList, inboundItem);
466 if (parentItem != null) {
467 removeFromList(parentList, parentItem); //exists, just take it off delete list
469 actionList.add(inboundItem); //doesn't exist as a parent, but is a parent. Add to additions list
472 logger.warn("Element didn't match parent or child, but may have partial fields that match. inboundItem: " + inboundItem);
473 //not dealing with: hasNarrower or any other predicate.
476 String dump = dumpLists(itemCSID, parentList, childList, actionList);
477 //System.out.println("====dump====="+CR+dump);
478 logger.info("~~~~~~~~~~~~~~~~~~~~~~dump~~~~~~~~~~~~~~~~~~~~~~~~" + CR + dump);
479 deleteRelations(parentList, ctx, "parentList"); //todo: there are items appearing on both lists....april 20.
480 deleteRelations(childList, ctx, "childList");
481 createRelations(actionList, ctx);
482 //We return all elements on the inbound list, since we have just worked to make them exist in the system
483 // and be non-redundant, etc. That list came from relationsCommonListBody, so it is still attached to it, just pass that back.
484 return relationsCommonListBody;
487 private void ensureChildHasNoOtherParents(ServiceContext ctx, MultivaluedMap queryParams, String childCSID) {
488 queryParams.putSingle(IRelationsManager.SUBJECT_QP, childCSID);
489 queryParams.putSingle(IRelationsManager.PREDICATE_QP, RelationshipType.HAS_BROADER.value());
490 queryParams.putSingle(IRelationsManager.OBJECT_QP, null); //null means ANY
491 RelationsCommonList parentListOuter = (new RelationResource()).getList(ctx.getUriInfo());
492 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
493 //logger.warn("ensureChildHasNoOtherParents preparing to delete relations on "+childCSID+"\'s parent list: \r\n"+dumpList(parentList, "duplicate parent list"));
494 deleteRelations(parentList, ctx, "parentList-delete");
497 private String dumpLists(String itemCSID,
498 List<RelationsCommonList.RelationListItem> parentList,
499 List<RelationsCommonList.RelationListItem> childList,
500 List<RelationsCommonList.RelationListItem> actionList) {
501 StringBuffer sb = new StringBuffer();
502 sb.append("itemCSID: " + itemCSID + CR);
503 sb.append(dumpList(parentList, "parentList"));
504 sb.append(dumpList(childList, "childList"));
505 sb.append(dumpList(actionList, "actionList"));
506 return sb.toString();
508 private final static String CR = "\r\n";
509 private final static String T = " ";
511 private String dumpList(List<RelationsCommonList.RelationListItem> list, String label) {
512 StringBuffer sb = new StringBuffer();
514 if (list.size() > 0) {
515 sb.append("=========== " + label + " ==========" + CR);
517 for (RelationsCommonList.RelationListItem item : list) {
519 T + item.getSubject().getCsid() //+T4 + item.getSubject().getUri()
520 + T + item.getPredicate()
521 + T + item.getObject().getCsid() //+T4 + item.getObject().getUri()
522 + CR //+"subject:{"+item.getSubject()+"}\r\n object:{"+item.getObject()+"}"
523 //+ CR + "relation-record: {"+item+"}"
528 return sb.toString();
531 /** Performs substitution for ${itemCSID} (see CommonAPI.AuthorityItemCSID_REPLACE for constant)
532 * and sets URI correctly for related items.
533 * Operates directly on the items in the list. Does not change the list ordering, does not add or remove any items.
535 protected void fixupInboundListItems(ServiceContext ctx,
536 List<RelationsCommonList.RelationListItem> inboundList,
537 DocumentModel docModel,
538 String itemCSID) throws Exception {
539 String thisURI = this.getUri(docModel);
540 // WARNING: the two code blocks below are almost identical and seem to ask to be put in a generic method.
541 // beware of the little diffs in inboundItem.setObjectCsid(itemCSID); and inboundItem.setSubjectCsid(itemCSID); in the two blocks.
542 for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
543 RelationsDocListItem inboundItemObject = inboundItem.getObject();
544 RelationsDocListItem inboundItemSubject = inboundItem.getSubject();
546 if (inboundItemObject.getCsid().equalsIgnoreCase(CommonAPI.AuthorityItemCSID_REPLACE)) {
547 inboundItem.setObjectCsid(itemCSID);
548 inboundItemObject.setCsid(itemCSID);
549 inboundItemObject.setUri(getUri(docModel));
551 String objectCsid = inboundItemObject.getCsid();
552 DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, objectCsid); //null if not found.
553 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
554 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
555 inboundItemObject.setUri(uri); //CSPACE-4037
557 uriPointsToSameAuthority(thisURI, inboundItemObject.getUri()); //CSPACE-4042
559 if (inboundItemSubject.getCsid().equalsIgnoreCase(CommonAPI.AuthorityItemCSID_REPLACE)) {
560 inboundItem.setSubjectCsid(itemCSID);
561 inboundItemSubject.setCsid(itemCSID);
562 inboundItemSubject.setUri(getUri(docModel));
564 String subjectCsid = inboundItemSubject.getCsid();
565 DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, subjectCsid); //null if not found.
566 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
567 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
568 inboundItemSubject.setUri(uri); //CSPACE-4037
570 uriPointsToSameAuthority(thisURI, inboundItemSubject.getUri()); //CSPACE-4042
575 public RepositoryClient getRepositoryClient(ServiceContext ctx) {
576 RepositoryClient repositoryClient = RepositoryClientFactory.getInstance().getClient(ctx.getRepositoryClientName());
577 return repositoryClient;
580 // this method calls the RelationResource to have it create the relations and persist them.
581 private void createRelations(List<RelationsCommonList.RelationListItem> inboundList, ServiceContext ctx) {
582 for (RelationsCommonList.RelationListItem item : inboundList) {
583 RelationsCommon rc = new RelationsCommon();
584 //rc.setCsid(item.getCsid());
585 //todo: assignTo(item, rc);
586 RelationsDocListItem itemSubject = item.getSubject();
587 RelationsDocListItem itemObject = item.getObject();
589 String subjectCsid = itemSubject.getCsid();
590 rc.setDocumentId1(subjectCsid);
591 rc.setSubjectCsid(subjectCsid);
593 String objCsid = item.getObject().getCsid();
594 rc.setDocumentId2(objCsid);
595 rc.setObjectCsid(objCsid);
597 rc.setRelationshipType(item.getPredicate());
598 //RelationshipType foo = (RelationshipType.valueOf(item.getPredicate())) ;
599 //rc.setPredicate(foo); //this must be one of the type found in the enum in services/jaxb/src/main/resources/relations_common.xsd
601 rc.setDocumentType1(itemSubject.getDocumentType());
602 rc.setDocumentType2(itemObject.getDocumentType());
604 rc.setSubjectUri(itemSubject.getUri());
605 rc.setObjectUri(itemObject.getUri());
608 PoxPayloadOut payloadOut = new PoxPayloadOut(RelationClient.SERVICE_PAYLOAD_NAME);
609 PayloadOutputPart outputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMONPART_NAME, rc);
610 payloadOut.addPart(outputPart);
611 //System.out.println("\r\n==== TO CREATE: "+rc.getDocumentId1()+"==>"+rc.getPredicate()+"==>"+rc.getDocumentId2());
612 RelationResource relationResource = new RelationResource();
613 Object res = relationResource.create(ctx.getUriInfo(), payloadOut.toXML()); //NOTE ui recycled from above to pass in unknown query params.
617 private void deleteRelations(List<RelationsCommonList.RelationListItem> list, ServiceContext ctx, String listName) {
619 //if (list.size()>0){ logger.info("==== deleteRelations from : "+listName); }
620 for (RelationsCommonList.RelationListItem item : list) {
621 RelationResource relationResource = new RelationResource();
622 //logger.info("==== TO DELETE: " + item.getCsid() + ": " + item.getSubject().getCsid() + "--" + item.getPredicate() + "-->" + item.getObject().getCsid());
623 Object res = relationResource.delete(item.getCsid());
625 } catch (Throwable t) {
626 String msg = "Unable to deleteRelations: " + Tools.errorToString(t, true);
631 private List<RelationsCommonList.RelationListItem> newList() {
632 List<RelationsCommonList.RelationListItem> result = new ArrayList<RelationsCommonList.RelationListItem>();
636 protected List<RelationsCommonList.RelationListItem> cloneList(List<RelationsCommonList.RelationListItem> inboundList) {
637 List<RelationsCommonList.RelationListItem> result = newList();
638 for (RelationsCommonList.RelationListItem item : inboundList) {
644 private RelationsCommonList.RelationListItem findInList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item) {
645 for (RelationsCommonList.RelationListItem listItem : list) {
646 if (itemsEqual(listItem, item)) { //equals must be defined, else
653 private boolean itemsEqual(RelationsCommonList.RelationListItem item, RelationsCommonList.RelationListItem item2) {
654 if (item == null || item2 == null) {
657 RelationsDocListItem subj1 = item.getSubject();
658 RelationsDocListItem subj2 = item2.getSubject();
659 RelationsDocListItem obj1 = item.getObject();
660 RelationsDocListItem obj2 = item2.getObject();
662 return (subj1.getCsid().equals(subj2.getCsid()))
663 && (obj1.getCsid().equals(obj1.getCsid()))
664 && ((item.getPredicate().equals(item2.getPredicate()))
665 && (item.getRelationshipType().equals(item2.getRelationshipType())))
666 && (obj1.getDocumentType().equals(obj2.getDocumentType()))
667 && (subj1.getDocumentType().equals(subj2.getDocumentType()));
670 private void removeFromList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item) {
674 /* don't even THINK of re-using this method.
675 * String example_uri = "/locationauthorities/7ec60f01-84ab-4908-9a6a/items/a5466530-713f-43b4-bc05";
677 private String extractInAuthorityCSID(String uri) {
678 String IN_AUTHORITY_REGEX = "/(.*?)/(.*?)/(.*)";
679 Pattern p = Pattern.compile(IN_AUTHORITY_REGEX);
680 Matcher m = p.matcher(uri);
682 if (m.groupCount() < 3) {
683 logger.warn("REGEX-WRONG-GROUPCOUNT looking in " + uri);
686 //String service = m.group(1);
687 String inauth = m.group(2);
688 //String theRest = m.group(3);
690 //print("service:"+service+", inauth:"+inauth+", rest:"+rest);
693 logger.warn("REGEX-NOT-MATCHED looking in " + uri);
698 //ensures CSPACE-4042
699 protected void uriPointsToSameAuthority(String thisURI, String inboundItemURI) throws Exception {
700 String authorityCSID = extractInAuthorityCSID(thisURI);
701 String authorityCSIDForInbound = extractInAuthorityCSID(inboundItemURI);
702 if (Tools.isBlank(authorityCSID)
703 || Tools.isBlank(authorityCSIDForInbound)
704 || (!authorityCSID.equalsIgnoreCase(authorityCSIDForInbound))) {
705 throw new Exception("Item URI " + thisURI + " must point to same authority as related item: " + inboundItemURI);
709 //================= TODO: move this to common, refactoring this and CollectionObjectResource.java
710 public RelationsCommonList getRelations(String subjectCSID, String objectCSID, String predicate) throws Exception {
711 ServiceContext ctx = getServiceContext();
712 MultivaluedMap queryParams = ctx.getQueryParams();
713 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
714 queryParams.putSingle(IRelationsManager.SUBJECT_QP, subjectCSID);
715 queryParams.putSingle(IRelationsManager.OBJECT_QP, objectCSID);
717 RelationResource relationResource = new RelationResource();
718 RelationsCommonList relationsCommonList = relationResource.getList(ctx.getUriInfo());
719 return relationsCommonList;
721 //============================= END TODO refactor ==========================