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 selected values supplied in an update request.
224 * For example, filters out AuthorityItemJAXBSchema.IN_AUTHORITY, to ensure
225 * that the link to the item's parent remains untouched.
227 * @param objectProps the properties filtered out from the update payload
228 * @param partMeta metadata for the object to fill
231 public void filterReadOnlyPropertiesForPart(
232 Map<String, Object> objectProps, ObjectPartType partMeta) {
233 super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
234 String commonPartLabel = getServiceContext().getCommonPartLabel();
235 if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
236 objectProps.remove(AuthorityItemJAXBSchema.IN_AUTHORITY);
237 objectProps.remove(AuthorityItemJAXBSchema.CSID);
238 objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
239 // Enable when clients should no longer supply refName values
240 // objectProps.remove(AuthorityItemJAXBSchema.REF_NAME); // CSPACE-3178
246 public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
247 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
248 super.extractAllParts(wrapDoc);
250 String showSiblings = ctx.getQueryParams().getFirst(CommonAPI.showSiblings_QP);
251 if (Tools.isTrue(showSiblings)) {
252 showSiblings(wrapDoc, ctx);
253 return; // actual result is returned on ctx.addOutputPart();
256 String showRelations = ctx.getQueryParams().getFirst(CommonAPI.showRelations_QP);
257 if (Tools.isTrue(showRelations)) {
258 showRelations(wrapDoc, ctx);
259 return; // actual result is returned on ctx.addOutputPart();
262 String showAllRelations = ctx.getQueryParams().getFirst(CommonAPI.showAllRelations_QP);
263 if (Tools.isTrue(showAllRelations)) {
264 showAllRelations(wrapDoc, ctx);
265 return; // actual result is returned on ctx.addOutputPart();
269 /** @return null on parent not found
271 protected String getParentCSID(String thisCSID) throws Exception {
272 String parentCSID = null;
274 String predicate = RelationshipType.HAS_BROADER.value();
275 RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
276 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
277 if (parentList != null) {
278 if (parentList.size() == 0) {
281 RelationsCommonList.RelationListItem relationListItem = parentList.get(0);
282 parentCSID = relationListItem.getObjectCsid();
285 } catch (Exception e) {
286 logger.error("Could not find parent for this: " + thisCSID, e);
291 public void showRelations(DocumentWrapper<DocumentModel> wrapDoc,
292 MultipartServiceContext ctx) throws Exception {
293 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
295 String predicate = RelationshipType.HAS_BROADER.value();
296 RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
297 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
299 RelationsCommonList childrenListOuter = getRelations(null, thisCSID, predicate);
300 List<RelationsCommonList.RelationListItem> childrenList = childrenListOuter.getRelationListItem();
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.
306 for (RelationsCommonList.RelationListItem parent : parentList) {
307 childrenList.add(parent);
310 long childrenSize = childrenList.size();
311 childrenListOuter.setTotalItems(childrenSize);
312 childrenListOuter.setItemsInPage(childrenListOuter.getItemsInPage() + added);
314 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, childrenListOuter);
315 ctx.addOutputPart(relationsPart);
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);
327 String predicate = RelationshipType.HAS_BROADER.value();
328 RelationsCommonList siblingListOuter = getRelations(null, parentCSID, predicate);
329 List<RelationsCommonList.RelationListItem> siblingList = siblingListOuter.getRelationListItem();
331 List<RelationsCommonList.RelationListItem> toRemoveList = newList();
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.
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);
345 long siblingSize = siblingList.size();
346 siblingListOuter.setTotalItems(siblingSize);
347 siblingListOuter.setItemsInPage(siblingSize);
349 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, siblingListOuter);
350 ctx.addOutputPart(relationsPart);
353 public void showAllRelations(DocumentWrapper<DocumentModel> wrapDoc, MultipartServiceContext ctx) throws Exception {
354 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
356 RelationsCommonList subjectListOuter = getRelations(thisCSID, null, null); // nulls are wildcards: predicate=*, and object=*
357 List<RelationsCommonList.RelationListItem> subjectList = subjectListOuter.getRelationListItem();
359 RelationsCommonList objectListOuter = getRelations(null, thisCSID, null); // nulls are wildcards: subject=*, and predicate=*
360 List<RelationsCommonList.RelationListItem> objectList = objectListOuter.getRelationListItem();
363 subjectList.addAll(objectList);
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);
370 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, subjectListOuter);
371 ctx.addOutputPart(relationsPart);
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();
381 //UPDATE and CREATE will call. Updates relations part
382 RelationsCommonList relationsCommonList = updateRelations(itemCsid, input, wrapDoc);
384 PayloadOutputPart payloadOutputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, relationsCommonList);
385 ctx.setProperty(RelationClient.SERVICE_COMMON_LIST_NAME, payloadOutputPart);
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);
396 /** updateRelations strategy:
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.
406 inboundList parentList childList actionList
407 ---------------- --------------- ---------------- ----------------
408 child-a parent-c child-a child-b
409 child-b parent-d child-c
412 public RelationsCommonList updateRelations(String itemCSID, PoxPayloadIn input, DocumentWrapper<DocumentModel> wrapDoc)
414 PayloadInputPart part = input.getPart(RelationClient.SERVICE_COMMON_LIST_NAME); //input.getPart("relations_common");
416 return null; //nothing to do--they didn't send a list of relations.
418 RelationsCommonList relationsCommonListBody = (RelationsCommonList) part.getBody();
420 ServiceContext ctx = getServiceContext();
421 UriInfo uriInfo = ctx.getUriInfo();
422 MultivaluedMap queryParams = uriInfo.getQueryParameters();
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.
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());
439 String HAS_BROADER = RelationshipType.HAS_BROADER.value();
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();
446 if (parentList.size() > 1) {
447 throw new Exception("Too many parents for object: " + itemCSID + " list: " + dumpList(parentList, "parentList"));
450 DocumentModel docModel = wrapDoc.getWrappedObject();
452 //Do magic replacement of ${itemCSID} and fix URI's.
453 fixupInboundListItems(ctx, inboundList, docModel, itemCSID);
455 for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
456 if (inboundItem.getObject().getCsid().equals(itemCSID) && inboundItem.getPredicate().equals(HAS_BROADER)) {
457 //then this is an item that says we have a child. That child is inboundItem
458 RelationsCommonList.RelationListItem childItem = findInList(childList, inboundItem);
459 if (childItem != null) {
460 removeFromList(childList, childItem); //exists, just take it off delete list
462 actionList.add(inboundItem); //doesn't exist as a child, but is a child. Add to additions list
464 ensureChildHasNoOtherParents(ctx, queryParams, inboundItem.getSubject().getCsid());
466 } else if (inboundItem.getSubject().getCsid().equals(itemCSID) && inboundItem.getPredicate().equals(HAS_BROADER)) {
467 //then this is an item that says we have a parent. inboundItem is that parent.
468 RelationsCommonList.RelationListItem parentItem = findInList(parentList, inboundItem);
469 if (parentItem != null) {
470 removeFromList(parentList, parentItem); //exists, just take it off delete list
472 actionList.add(inboundItem); //doesn't exist as a parent, but is a parent. Add to additions list
475 logger.warn("Element didn't match parent or child, but may have partial fields that match. inboundItem: " + inboundItem);
476 //not dealing with: hasNarrower or any other predicate.
479 String dump = dumpLists(itemCSID, parentList, childList, actionList);
480 //System.out.println("====dump====="+CR+dump);
481 logger.info("~~~~~~~~~~~~~~~~~~~~~~dump~~~~~~~~~~~~~~~~~~~~~~~~" + CR + dump);
482 deleteRelations(parentList, ctx, "parentList"); //todo: there are items appearing on both lists....april 20.
483 deleteRelations(childList, ctx, "childList");
484 createRelations(actionList, ctx);
485 //We return all elements on the inbound list, since we have just worked to make them exist in the system
486 // and be non-redundant, etc. That list came from relationsCommonListBody, so it is still attached to it, just pass that back.
487 return relationsCommonListBody;
490 private void ensureChildHasNoOtherParents(ServiceContext ctx, MultivaluedMap queryParams, String childCSID) {
491 queryParams.putSingle(IRelationsManager.SUBJECT_QP, childCSID);
492 queryParams.putSingle(IRelationsManager.PREDICATE_QP, RelationshipType.HAS_BROADER.value());
493 queryParams.putSingle(IRelationsManager.OBJECT_QP, null); //null means ANY
494 RelationsCommonList parentListOuter = (new RelationResource()).getList(ctx.getUriInfo());
495 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
496 //logger.warn("ensureChildHasNoOtherParents preparing to delete relations on "+childCSID+"\'s parent list: \r\n"+dumpList(parentList, "duplicate parent list"));
497 deleteRelations(parentList, ctx, "parentList-delete");
500 private String dumpLists(String itemCSID,
501 List<RelationsCommonList.RelationListItem> parentList,
502 List<RelationsCommonList.RelationListItem> childList,
503 List<RelationsCommonList.RelationListItem> actionList) {
504 StringBuffer sb = new StringBuffer();
505 sb.append("itemCSID: " + itemCSID + CR);
506 sb.append(dumpList(parentList, "parentList"));
507 sb.append(dumpList(childList, "childList"));
508 sb.append(dumpList(actionList, "actionList"));
509 return sb.toString();
511 private final static String CR = "\r\n";
512 private final static String T = " ";
514 private String dumpList(List<RelationsCommonList.RelationListItem> list, String label) {
515 StringBuffer sb = new StringBuffer();
517 if (list.size() > 0) {
518 sb.append("=========== " + label + " ==========" + CR);
520 for (RelationsCommonList.RelationListItem item : list) {
522 T + item.getSubject().getCsid() //+T4 + item.getSubject().getUri()
523 + T + item.getPredicate()
524 + T + item.getObject().getCsid() //+T4 + item.getObject().getUri()
525 + CR //+"subject:{"+item.getSubject()+"}\r\n object:{"+item.getObject()+"}"
526 //+ CR + "relation-record: {"+item+"}"
531 return sb.toString();
534 /** Performs substitution for ${itemCSID} (see CommonAPI.AuthorityItemCSID_REPLACE for constant)
535 * and sets URI correctly for related items.
536 * Operates directly on the items in the list. Does not change the list ordering, does not add or remove any items.
538 protected void fixupInboundListItems(ServiceContext ctx,
539 List<RelationsCommonList.RelationListItem> inboundList,
540 DocumentModel docModel,
541 String itemCSID) throws Exception {
542 String thisURI = this.getUri(docModel);
543 // WARNING: the two code blocks below are almost identical and seem to ask to be put in a generic method.
544 // beware of the little diffs in inboundItem.setObjectCsid(itemCSID); and inboundItem.setSubjectCsid(itemCSID); in the two blocks.
545 for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
546 RelationsDocListItem inboundItemObject = inboundItem.getObject();
547 RelationsDocListItem inboundItemSubject = inboundItem.getSubject();
549 if (inboundItemObject.getCsid().equalsIgnoreCase(CommonAPI.AuthorityItemCSID_REPLACE)) {
550 inboundItem.setObjectCsid(itemCSID);
551 inboundItemObject.setCsid(itemCSID);
552 inboundItemObject.setUri(getUri(docModel));
554 String objectCsid = inboundItemObject.getCsid();
555 DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, objectCsid); //null if not found.
556 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
557 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
558 inboundItemObject.setUri(uri); //CSPACE-4037
560 uriPointsToSameAuthority(thisURI, inboundItemObject.getUri()); //CSPACE-4042
562 if (inboundItemSubject.getCsid().equalsIgnoreCase(CommonAPI.AuthorityItemCSID_REPLACE)) {
563 inboundItem.setSubjectCsid(itemCSID);
564 inboundItemSubject.setCsid(itemCSID);
565 inboundItemSubject.setUri(getUri(docModel));
567 String subjectCsid = inboundItemSubject.getCsid();
568 DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, subjectCsid); //null if not found.
569 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
570 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
571 inboundItemSubject.setUri(uri); //CSPACE-4037
573 uriPointsToSameAuthority(thisURI, inboundItemSubject.getUri()); //CSPACE-4042
578 public RepositoryClient getRepositoryClient(ServiceContext ctx) {
579 RepositoryClient repositoryClient = RepositoryClientFactory.getInstance().getClient(ctx.getRepositoryClientName());
580 return repositoryClient;
583 // this method calls the RelationResource to have it create the relations and persist them.
584 private void createRelations(List<RelationsCommonList.RelationListItem> inboundList, ServiceContext ctx) {
585 for (RelationsCommonList.RelationListItem item : inboundList) {
586 RelationsCommon rc = new RelationsCommon();
587 //rc.setCsid(item.getCsid());
588 //todo: assignTo(item, rc);
589 RelationsDocListItem itemSubject = item.getSubject();
590 RelationsDocListItem itemObject = item.getObject();
592 String subjectCsid = itemSubject.getCsid();
593 rc.setDocumentId1(subjectCsid);
594 rc.setSubjectCsid(subjectCsid);
596 String objCsid = item.getObject().getCsid();
597 rc.setDocumentId2(objCsid);
598 rc.setObjectCsid(objCsid);
600 rc.setRelationshipType(item.getPredicate());
601 //RelationshipType foo = (RelationshipType.valueOf(item.getPredicate())) ;
602 //rc.setPredicate(foo); //this must be one of the type found in the enum in services/jaxb/src/main/resources/relations_common.xsd
604 rc.setDocumentType1(itemSubject.getDocumentType());
605 rc.setDocumentType2(itemObject.getDocumentType());
607 rc.setSubjectUri(itemSubject.getUri());
608 rc.setObjectUri(itemObject.getUri());
611 PoxPayloadOut payloadOut = new PoxPayloadOut(RelationClient.SERVICE_PAYLOAD_NAME);
612 PayloadOutputPart outputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMONPART_NAME, rc);
613 payloadOut.addPart(outputPart);
614 //System.out.println("\r\n==== TO CREATE: "+rc.getDocumentId1()+"==>"+rc.getPredicate()+"==>"+rc.getDocumentId2());
615 RelationResource relationResource = new RelationResource();
616 Object res = relationResource.create(ctx.getUriInfo(), payloadOut.toXML()); //NOTE ui recycled from above to pass in unknown query params.
620 private void deleteRelations(List<RelationsCommonList.RelationListItem> list, ServiceContext ctx, String listName) {
622 //if (list.size()>0){ logger.info("==== deleteRelations from : "+listName); }
623 for (RelationsCommonList.RelationListItem item : list) {
624 RelationResource relationResource = new RelationResource();
625 //logger.info("==== TO DELETE: " + item.getCsid() + ": " + item.getSubject().getCsid() + "--" + item.getPredicate() + "-->" + item.getObject().getCsid());
626 Object res = relationResource.delete(item.getCsid());
628 } catch (Throwable t) {
629 String msg = "Unable to deleteRelations: " + Tools.errorToString(t, true);
634 private List<RelationsCommonList.RelationListItem> newList() {
635 List<RelationsCommonList.RelationListItem> result = new ArrayList<RelationsCommonList.RelationListItem>();
639 protected List<RelationsCommonList.RelationListItem> cloneList(List<RelationsCommonList.RelationListItem> inboundList) {
640 List<RelationsCommonList.RelationListItem> result = newList();
641 for (RelationsCommonList.RelationListItem item : inboundList) {
647 private RelationsCommonList.RelationListItem findInList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item) {
648 for (RelationsCommonList.RelationListItem listItem : list) {
649 if (itemsEqual(listItem, item)) { //equals must be defined, else
656 private boolean itemsEqual(RelationsCommonList.RelationListItem item, RelationsCommonList.RelationListItem item2) {
657 if (item == null || item2 == null) {
660 RelationsDocListItem subj1 = item.getSubject();
661 RelationsDocListItem subj2 = item2.getSubject();
662 RelationsDocListItem obj1 = item.getObject();
663 RelationsDocListItem obj2 = item2.getObject();
665 return (subj1.getCsid().equals(subj2.getCsid()))
666 && (obj1.getCsid().equals(obj1.getCsid()))
667 && ((item.getPredicate().equals(item2.getPredicate()))
668 && (item.getRelationshipType().equals(item2.getRelationshipType())))
669 && (obj1.getDocumentType().equals(obj2.getDocumentType()))
670 && (subj1.getDocumentType().equals(subj2.getDocumentType()));
673 private void removeFromList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item) {
677 /* don't even THINK of re-using this method.
678 * String example_uri = "/locationauthorities/7ec60f01-84ab-4908-9a6a/items/a5466530-713f-43b4-bc05";
680 private String extractInAuthorityCSID(String uri) {
681 String IN_AUTHORITY_REGEX = "/(.*?)/(.*?)/(.*)";
682 Pattern p = Pattern.compile(IN_AUTHORITY_REGEX);
683 Matcher m = p.matcher(uri);
685 if (m.groupCount() < 3) {
686 logger.warn("REGEX-WRONG-GROUPCOUNT looking in " + uri);
689 //String service = m.group(1);
690 String inauth = m.group(2);
691 //String theRest = m.group(3);
693 //print("service:"+service+", inauth:"+inauth+", rest:"+rest);
696 logger.warn("REGEX-NOT-MATCHED looking in " + uri);
701 //ensures CSPACE-4042
702 protected void uriPointsToSameAuthority(String thisURI, String inboundItemURI) throws Exception {
703 String authorityCSID = extractInAuthorityCSID(thisURI);
704 String authorityCSIDForInbound = extractInAuthorityCSID(inboundItemURI);
705 if (Tools.isBlank(authorityCSID)
706 || Tools.isBlank(authorityCSIDForInbound)
707 || (!authorityCSID.equalsIgnoreCase(authorityCSIDForInbound))) {
708 throw new Exception("Item URI " + thisURI + " must point to same authority as related item: " + inboundItemURI);
712 //================= TODO: move this to common, refactoring this and CollectionObjectResource.java
713 public RelationsCommonList getRelations(String subjectCSID, String objectCSID, String predicate) throws Exception {
714 ServiceContext ctx = getServiceContext();
715 MultivaluedMap queryParams = ctx.getQueryParams();
716 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
717 queryParams.putSingle(IRelationsManager.SUBJECT_QP, subjectCSID);
718 queryParams.putSingle(IRelationsManager.OBJECT_QP, objectCSID);
720 RelationResource relationResource = new RelationResource();
721 RelationsCommonList relationsCommonList = relationResource.getList(ctx.getUriInfo());
722 return relationsCommonList;
724 //============================= END TODO refactor ==========================