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.nuxeo.client.java;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
30 import java.util.Map.Entry;
33 import javax.ws.rs.WebApplicationException;
34 import javax.ws.rs.core.MediaType;
35 import javax.ws.rs.core.MultivaluedMap;
36 import javax.ws.rs.core.Response;
37 import javax.ws.rs.core.UriInfo;
38 import javax.xml.bind.JAXBElement;
40 import org.collectionspace.services.authorization.AccountPermission;
41 import org.collectionspace.services.jaxb.AbstractCommonList;
42 import org.collectionspace.services.lifecycle.TransitionDef;
43 import org.collectionspace.services.client.CollectionSpaceClient;
44 import org.collectionspace.services.client.PayloadInputPart;
45 import org.collectionspace.services.client.PayloadOutputPart;
46 import org.collectionspace.services.client.PoxPayloadIn;
47 import org.collectionspace.services.client.PoxPayloadOut;
48 import org.collectionspace.services.client.Profiler;
49 import org.collectionspace.services.client.RelationClient;
50 import org.collectionspace.services.client.workflow.WorkflowClient;
51 import org.collectionspace.services.common.ResourceBase;
52 import org.collectionspace.services.common.authorityref.AuthorityRefList;
53 import org.collectionspace.services.common.context.JaxRsContext;
54 import org.collectionspace.services.common.context.MultipartServiceContext;
55 import org.collectionspace.services.common.context.ServiceBindingUtils;
56 import org.collectionspace.services.common.context.ServiceContext;
57 import org.collectionspace.services.common.document.BadRequestException;
58 import org.collectionspace.services.common.document.DocumentException;
59 import org.collectionspace.services.common.document.DocumentUtils;
60 import org.collectionspace.services.common.document.DocumentWrapper;
61 import org.collectionspace.services.common.document.DocumentFilter;
62 import org.collectionspace.services.client.IRelationsManager;
63 import org.collectionspace.services.common.relation.RelationResource;
64 import org.collectionspace.services.common.repository.RepositoryClient;
65 import org.collectionspace.services.common.security.SecurityUtils;
66 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
67 import org.collectionspace.services.common.api.CommonAPI;
68 import org.collectionspace.services.common.api.RefNameUtils;
69 import org.collectionspace.services.common.api.Tools;
70 import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
71 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils;
72 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthRefConfigInfo;
73 import org.collectionspace.services.config.service.DocHandlerParams;
74 import org.collectionspace.services.config.service.ListResultField;
75 import org.collectionspace.services.config.service.ObjectPartType;
76 import org.collectionspace.services.config.service.ServiceBindingType;
77 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
78 import org.collectionspace.services.relation.RelationsCommon;
79 import org.collectionspace.services.relation.RelationsCommonList;
80 import org.collectionspace.services.relation.RelationsDocListItem;
81 import org.collectionspace.services.relation.RelationshipType;
82 import org.dom4j.Element;
84 import org.nuxeo.ecm.core.api.DocumentModel;
85 import org.nuxeo.ecm.core.api.DocumentModelList;
86 import org.nuxeo.ecm.core.api.model.PropertyException;
87 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
89 import org.slf4j.Logger;
90 import org.slf4j.LoggerFactory;
93 * RemoteDocumentModelHandler
95 * $LastChangedRevision: $
100 public abstract class RemoteDocumentModelHandlerImpl<T, TL>
101 extends DocumentModelHandler<T, TL> {
104 private final Logger logger = LoggerFactory.getLogger(RemoteDocumentModelHandlerImpl.class);
105 private final static String CR = "\r\n";
107 protected String oldRefNameOnUpdate = null;
108 protected String newRefNameOnUpdate = null;
111 * @see org.collectionspace.services.common.document.AbstractDocumentHandlerImpl#setServiceContext(org.collectionspace.services.common.context.ServiceContext)
114 public void setServiceContext(ServiceContext ctx) { //FIXME: Apply proper generics to ServiceContext<PoxPayloadIn, PoxPayloadOut>
115 if (ctx instanceof MultipartServiceContext) {
116 super.setServiceContext(ctx);
118 throw new IllegalArgumentException("setServiceContext requires instance of "
119 + MultipartServiceContext.class.getName());
124 * Returns the document handler parameters that were loaded at startup from the
125 * tenant bindings config file.
127 public DocHandlerParams.Params getDocHandlerParams() throws DocumentException {
128 MultipartServiceContext sc = (MultipartServiceContext) getServiceContext();
129 ServiceBindingType sb = sc.getServiceBinding();
130 DocHandlerParams dhb = sb.getDocHandlerParams();
131 if (dhb != null && dhb.getParams() != null) {
132 return dhb.getParams();
134 throw new DocumentException("No DocHandlerParams configured for: "
139 public boolean supportsHierarchy() {
140 boolean result = false;
142 DocHandlerParams.Params params = null;
144 params = getDocHandlerParams();
145 Boolean bool = params.isSupportsHierarchy();
147 result = bool.booleanValue();
149 } catch (DocumentException e) {
150 // TODO Auto-generated catch block
151 logger.error(String.format("Could not get document handler params for class %s", this.getClass().getName()), e);
158 public void handleWorkflowTransition(DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef)
160 // Do nothing by default, but children can override if they want. The really workflow transition happens in the WorkflowDocumemtModelHandler class
164 public void completeCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
165 super.completeCreate(wrapDoc);
166 if (supportsHierarchy() == true) {
167 handleRelationsPayload(wrapDoc, false);
172 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#completeUpdate(org.collectionspace.services.common.document.DocumentWrapper)
175 public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
176 DocumentModel docModel = wrapDoc.getWrappedObject();
177 //return at least those document part(s) that were received
178 Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
179 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
180 PoxPayloadIn input = ctx.getInput();
182 List<PayloadInputPart> inputParts = ctx.getInput().getParts();
183 for (PayloadInputPart part : inputParts) {
184 String partLabel = part.getLabel();
186 ObjectPartType partMeta = partsMetaMap.get(partLabel);
187 // CSPACE-4030 - generates NPE if the part is missing.
189 Map<String, Object> unQObjectProperties = extractPart(docModel, partLabel, partMeta);
190 if(unQObjectProperties!=null) {
191 addOutputPart(unQObjectProperties, partLabel, partMeta);
194 } catch (Throwable t){
195 logger.error("Unable to addOutputPart: "+partLabel
196 +" in serviceContextPath: "+this.getServiceContextPath()
197 +" with URI: "+this.getServiceContext().getUriInfo().getPath()
202 if (logger.isWarnEnabled() == true) {
203 logger.warn("MultipartInput part was null for document id = " +
208 if (supportsHierarchy() == true) {
209 handleRelationsPayload(wrapDoc, true);
210 handleItemRefNameReferenceUpdate();
215 * Adds the output part.
217 * @param unQObjectProperties the un q object properties
218 * @param schema the schema
219 * @param partMeta the part meta
220 * @throws Exception the exception
221 * MediaType.APPLICATION_XML_TYPE
223 protected void addOutputPart(Map<String, Object> unQObjectProperties, String schema, ObjectPartType partMeta)
225 Element doc = DocumentUtils.buildDocument(partMeta, schema,
226 unQObjectProperties);
227 if (logger.isTraceEnabled() == true) {
228 logger.trace(doc.asXML());
230 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
231 ctx.addOutputPart(schema, doc, partMeta.getContent().getContentType());
235 * Extract paging info.
237 * @param commonsList the commons list
239 * @throws Exception the exception
241 public TL extractPagingInfo(TL theCommonList, DocumentWrapper<DocumentModelList> wrapDoc)
243 AbstractCommonList commonList = (AbstractCommonList) theCommonList;
245 DocumentFilter docFilter = this.getDocumentFilter();
246 long pageSize = docFilter.getPageSize();
247 long pageNum = pageSize != 0 ? docFilter.getOffset() / pageSize : pageSize;
248 // set the page size and page number
249 commonList.setPageNum(pageNum);
250 commonList.setPageSize(pageSize);
251 DocumentModelList docList = wrapDoc.getWrappedObject();
252 // Set num of items in list. this is useful to our testing framework.
253 commonList.setItemsInPage(docList.size());
254 // set the total result size
255 commonList.setTotalItems(docList.totalSize());
257 return (TL) commonList;
261 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#extractAllParts(org.collectionspace.services.common.document.DocumentWrapper)
264 public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc)
267 DocumentModel docModel = wrapDoc.getWrappedObject();
268 String[] schemas = docModel.getDeclaredSchemas();
269 Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
270 for (String schema : schemas) {
271 ObjectPartType partMeta = partsMetaMap.get(schema);
272 if (partMeta == null) {
273 continue; // unknown part, ignore
275 Map<String, Object> unQObjectProperties = extractPart(docModel, schema, partMeta);
276 if(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA.equals(schema)) {
277 addExtraCoreValues(docModel, unQObjectProperties);
279 addOutputPart(unQObjectProperties, schema, partMeta);
282 if (supportsHierarchy() == true) {
283 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
284 String showSiblings = ctx.getQueryParams().getFirst(CommonAPI.showSiblings_QP);
285 if (Tools.isTrue(showSiblings)) {
286 showSiblings(wrapDoc, ctx);
287 return; // actual result is returned on ctx.addOutputPart();
290 String showRelations = ctx.getQueryParams().getFirst(CommonAPI.showRelations_QP);
291 if (Tools.isTrue(showRelations)) {
292 showRelations(wrapDoc, ctx);
293 return; // actual result is returned on ctx.addOutputPart();
296 String showAllRelations = ctx.getQueryParams().getFirst(CommonAPI.showAllRelations_QP);
297 if (Tools.isTrue(showAllRelations)) {
298 showAllRelations(wrapDoc, ctx);
299 return; // actual result is returned on ctx.addOutputPart();
303 addAccountPermissionsPart();
306 private void addExtraCoreValues(DocumentModel docModel, Map<String, Object> unQObjectProperties)
308 unQObjectProperties.put(CollectionSpaceClient.COLLECTIONSPACE_CORE_WORKFLOWSTATE, docModel.getCurrentLifeCycleState());
311 private void addAccountPermissionsPart() throws Exception {
312 Profiler profiler = new Profiler("addAccountPermissionsPart():", 1);
315 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
316 String currentServiceName = ctx.getServiceName();
317 String workflowSubResource = "/";
318 JaxRsContext jaxRsContext = ctx.getJaxRsContext();
319 if (jaxRsContext != null) {
320 String resourceName = SecurityUtils.getResourceName(jaxRsContext.getUriInfo());
321 workflowSubResource = workflowSubResource + resourceName + WorkflowClient.SERVICE_PATH + "/";
323 workflowSubResource = workflowSubResource + currentServiceName + WorkflowClient.SERVICE_AUTHZ_SUFFIX;
325 AccountPermission accountPermission = JpaStorageUtils.getAccountPermissions(JpaStorageUtils.CS_CURRENT_USER,
326 currentServiceName, workflowSubResource);
327 org.collectionspace.services.authorization.ObjectFactory objectFactory =
328 new org.collectionspace.services.authorization.ObjectFactory();
329 JAXBElement<AccountPermission> ap = objectFactory.createAccountPermission(accountPermission);
330 PayloadOutputPart accountPermissionPart = new PayloadOutputPart("account_permission", ap);
331 ctx.addOutputPart(accountPermissionPart);
337 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#fillAllParts(org.collectionspace.services.common.document.DocumentWrapper)
340 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
342 //TODO filling extension parts should be dynamic
343 //Nuxeo APIs lack to support stream/byte[] input, get/setting properties is
344 //not an ideal way of populating objects.
345 DocumentModel docModel = wrapDoc.getWrappedObject();
346 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
347 PoxPayloadIn input = ctx.getInput();
348 if (input.getParts().isEmpty()) {
349 String msg = "No payload found!";
350 logger.error(msg + "Ctx=" + getServiceContext().toString());
351 throw new BadRequestException(msg);
354 Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
356 //iterate over parts received and fill those parts
357 List<PayloadInputPart> inputParts = input.getParts();
358 for (PayloadInputPart part : inputParts) {
360 String partLabel = part.getLabel();
361 if (partLabel == null) {
362 String msg = "Part label is missing or empty!";
363 logger.error(msg + "Ctx=" + getServiceContext().toString());
364 throw new BadRequestException(msg);
367 //skip if the part is not in metadata
368 ObjectPartType partMeta = partsMetaMap.get(partLabel);
369 if (partMeta == null) {
372 fillPart(part, docModel, partMeta, action, ctx);
378 * fillPart fills an XML part into given document model
379 * @param part to fill
380 * @param docModel for the given object
381 * @param partMeta metadata for the object to fill
384 protected void fillPart(PayloadInputPart part, DocumentModel docModel,
385 ObjectPartType partMeta, Action action, ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
387 //check if this is an xml part
388 if (part.getMediaType().equals(MediaType.APPLICATION_XML_TYPE)) {
389 Element element = part.getElementBody();
390 Map<String, Object> objectProps = DocumentUtils.parseProperties(partMeta, element, ctx);
391 if (action == Action.UPDATE) {
392 this.filterReadOnlyPropertiesForPart(objectProps, partMeta);
394 docModel.setProperties(partMeta.getLabel(), objectProps);
399 * Filters out read only properties, so they cannot be set on update.
400 * TODO: add configuration support to do this generally
401 * @param objectProps the properties parsed from the update payload
402 * @param partMeta metadata for the object to fill
404 public void filterReadOnlyPropertiesForPart(
405 Map<String, Object> objectProps, ObjectPartType partMeta) {
406 // Should add in logic to filter most of the core items on update
407 if(partMeta.getLabel().equalsIgnoreCase(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA)) {
408 objectProps.remove(CollectionSpaceClient.COLLECTIONSPACE_CORE_CREATED_AT);
409 objectProps.remove(CollectionSpaceClient.COLLECTIONSPACE_CORE_CREATED_BY);
410 objectProps.remove(CollectionSpaceClient.COLLECTIONSPACE_CORE_URI);
411 objectProps.remove(CollectionSpaceClient.COLLECTIONSPACE_CORE_TENANTID);
412 // Note that the updatedAt/updatedBy fields are set internally
413 // in DocumentModelHandler.handleCoreValues().
418 * extractPart extracts an XML object from given DocumentModel
420 * @param schema of the object to extract
421 * @param partMeta metadata for the object to extract
424 protected Map<String, Object> extractPart(DocumentModel docModel, String schema)
426 return extractPart(docModel, schema, (Map<String, Object>)null);
430 * extractPart extracts an XML object from given DocumentModel
432 * @param schema of the object to extract
433 * @param partMeta metadata for the object to extract
437 protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
439 return extractPart(docModel, schema, partMeta, null);
443 * extractPart extracts an XML object from given DocumentModel
445 * @param schema of the object to extract
446 * @param partMeta metadata for the object to extract
449 protected Map<String, Object> extractPart(
450 DocumentModel docModel,
452 Map<String, Object> addToMap)
454 Map<String, Object> result = null;
456 Map<String, Object> objectProps = docModel.getProperties(schema);
457 if (objectProps != null) {
458 //unqualify properties before sending the doc over the wire (to save bandwidh)
459 //FIXME: is there a better way to avoid duplication of a Map/Collection?
460 Map<String, Object> unQObjectProperties =
461 (addToMap != null) ? addToMap : (new HashMap<String, Object>());
462 Set<Entry<String, Object>> qualifiedEntries = objectProps.entrySet();
463 for (Entry<String, Object> entry : qualifiedEntries) {
464 String unqProp = getUnQProperty(entry.getKey());
465 unQObjectProperties.put(unqProp, entry.getValue());
467 result = unQObjectProperties;
474 * extractPart extracts an XML object from given DocumentModel
476 * @param schema of the object to extract
477 * @param partMeta metadata for the object to extract
481 protected Map<String, Object> extractPart(
482 DocumentModel docModel, String schema, ObjectPartType partMeta,
483 Map<String, Object> addToMap)
485 Map<String, Object> result = null;
487 result = this.extractPart(docModel, schema, addToMap);
493 public String getStringPropertyFromDoc(
496 String propertyXPath ) throws DocumentNotFoundException, DocumentException {
497 RepositoryInstance repoSession = null;
498 boolean releaseRepoSession = false;
499 String returnValue = null;
502 RepositoryJavaClientImpl repoClient = (RepositoryJavaClientImpl)this.getRepositoryClient(ctx);
503 repoSession = this.getRepositorySession();
504 if (repoSession == null) {
505 repoSession = repoClient.getRepositorySession();
506 releaseRepoSession = true;
510 DocumentWrapper<DocumentModel> wrapper = repoClient.getDoc(repoSession, ctx, csid);
511 DocumentModel docModel = wrapper.getWrappedObject();
512 returnValue = (String) docModel.getPropertyValue(propertyXPath);
513 } catch (PropertyException pe) {
515 } catch (DocumentException de) {
517 } catch (Exception e) {
518 if (logger.isDebugEnabled()) {
519 logger.debug("Caught exception ", e);
521 throw new DocumentException(e);
523 if (releaseRepoSession && repoSession != null) {
524 repoClient.releaseRepositorySession(repoSession);
527 } catch (Exception e) {
528 if (logger.isDebugEnabled()) {
529 logger.debug("Caught exception ", e);
531 throw new DocumentException(e);
535 if (logger.isWarnEnabled() == true) {
536 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
545 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#getAuthorityRefs(org.collectionspace.services.common.document.DocumentWrapper, java.util.List)
548 public AuthorityRefList getAuthorityRefs(
550 List<AuthRefConfigInfo> authRefsInfo) throws PropertyException {
552 AuthorityRefList authRefList = new AuthorityRefList();
553 AbstractCommonList commonList = (AbstractCommonList) authRefList;
555 DocumentFilter docFilter = this.getDocumentFilter();
556 long pageSize = docFilter.getPageSize();
557 long pageNum = pageSize != 0 ? docFilter.getOffset() / pageSize : pageSize;
558 // set the page size and page number
559 commonList.setPageNum(pageNum);
560 commonList.setPageSize(pageSize);
562 List<AuthorityRefList.AuthorityRefItem> list = authRefList.getAuthorityRefItem();
565 int iFirstToUse = (int)(pageSize*pageNum);
566 int nFoundInPage = 0;
569 ArrayList<RefNameServiceUtils.AuthRefInfo> foundProps
570 = new ArrayList<RefNameServiceUtils.AuthRefInfo>();
572 boolean releaseRepoSession = false;
573 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
574 RepositoryJavaClientImpl repoClient = (RepositoryJavaClientImpl)this.getRepositoryClient(ctx);
575 RepositoryInstance repoSession = this.getRepositorySession();
576 if (repoSession == null) {
577 repoSession = repoClient.getRepositorySession(ctx);
578 releaseRepoSession = true;
582 DocumentModel docModel = repoClient.getDoc(repoSession, ctx, csid).getWrappedObject();
583 RefNameServiceUtils.findAuthRefPropertiesInDoc(docModel, authRefsInfo, null, foundProps);
584 // Slightly goofy pagination support - how many refs do we expect from one object?
585 for(RefNameServiceUtils.AuthRefInfo ari:foundProps) {
586 if((nFoundTotal >= iFirstToUse) && (nFoundInPage < pageSize)) {
587 if(appendToAuthRefsList(ari, list)) {
596 if (releaseRepoSession == true) {
597 repoClient.releaseRepositorySession(ctx, repoSession);
601 // Set num of items in list. this is useful to our testing framework.
602 commonList.setItemsInPage(nFoundInPage);
603 // set the total result size
604 commonList.setTotalItems(nFoundTotal);
606 } catch (PropertyException pe) {
607 String msg = "Attempted to retrieve value for invalid or missing authority field. "
608 + "Check authority field properties in tenant bindings.";
609 logger.warn(msg, pe);
611 } catch (Exception e) {
612 if (logger.isDebugEnabled()) {
613 logger.debug("Caught exception in getAuthorityRefs", e);
615 Response response = Response.status(
616 Response.Status.INTERNAL_SERVER_ERROR).entity(
617 "Failed to retrieve authority references").type(
618 "text/plain").build();
619 throw new WebApplicationException(response);
625 private boolean appendToAuthRefsList(RefNameServiceUtils.AuthRefInfo ari,
626 List<AuthorityRefList.AuthorityRefItem> list)
628 String fieldName = ari.getQualifiedDisplayName();
630 String refNameValue = (String)ari.getProperty().getValue();
631 AuthorityRefList.AuthorityRefItem item = authorityRefListItem(fieldName, refNameValue);
632 if(item!=null) { // ignore garbage values.
636 } catch(PropertyException pe) {
637 logger.debug("PropertyException on: "+ari.getProperty().getPath()+pe.getLocalizedMessage());
642 private AuthorityRefList.AuthorityRefItem authorityRefListItem(String authRefFieldName, String refName) {
644 AuthorityRefList.AuthorityRefItem ilistItem = new AuthorityRefList.AuthorityRefItem();
646 RefNameUtils.AuthorityTermInfo termInfo = RefNameUtils.parseAuthorityTermInfo(refName);
647 ilistItem.setRefName(refName);
648 ilistItem.setAuthDisplayName(termInfo.inAuthority.displayName);
649 ilistItem.setItemDisplayName(termInfo.displayName);
650 ilistItem.setSourceField(authRefFieldName);
651 ilistItem.setUri(termInfo.getRelativeUri());
652 } catch (Exception e) {
653 logger.error("Trouble parsing refName from value: "+refName+" in field: "+authRefFieldName+e.getLocalizedMessage());
660 * Returns the primary value from a list of values.
662 * Assumes that the first value is the primary value.
663 * This assumption may change when and if the primary value
664 * is identified explicitly.
666 * @param values a list of values.
667 * @param propertyName the name of a property through
668 * which the value can be extracted.
669 * @return the primary value.
670 protected String primaryValueFromMultivalue(List<Object> values, String propertyName) {
671 String primaryValue = "";
672 if (values == null || values.size() == 0) {
675 Object value = values.get(0);
676 if (value instanceof String) {
678 primaryValue = (String) value;
680 // Multivalue group of fields
681 } else if (value instanceof Map) {
683 Map map = (Map) value;
684 if (map.values().size() > 0) {
685 if (map.get(propertyName) != null) {
686 primaryValue = (String) map.get(propertyName);
691 logger.warn("Unexpected type for property " + propertyName
692 + " in multivalue list: not String or Map.");
699 * Gets a simple property from the document.
701 * For completeness, as this duplicates DocumentModel method.
703 * @param docModel The document model to get info from
704 * @param schema The name of the schema (part)
705 * @param propertyName The simple scalar property type
706 * @return property value as String
708 protected String getSimpleStringProperty(DocumentModel docModel, String schema, String propName) {
709 String xpath = "/"+schema+":"+propName;
711 return (String)docModel.getPropertyValue(xpath);
712 } catch(PropertyException pe) {
713 throw new RuntimeException("Problem retrieving property {"+xpath+"}. Not a simple String property?"
714 +pe.getLocalizedMessage());
715 } catch(ClassCastException cce) {
716 throw new RuntimeException("Problem retrieving property {"+xpath+"} as String. Not a scalar String property?"
717 +cce.getLocalizedMessage());
718 } catch(Exception e) {
719 throw new RuntimeException("Unknown problem retrieving property {"+xpath+"}."
720 +e.getLocalizedMessage());
725 * Gets first of a repeating list of scalar values, as a String, from the document.
727 * @param docModel The document model to get info from
728 * @param schema The name of the schema (part)
729 * @param listName The name of the scalar list property
730 * @return first value in list, as a String, or empty string if the list is empty
732 protected String getFirstRepeatingStringProperty(
733 DocumentModel docModel, String schema, String listName) {
734 String xpath = "/"+schema+":"+listName+"/[0]";
736 return (String)docModel.getPropertyValue(xpath);
737 } catch(PropertyException pe) {
738 throw new RuntimeException("Problem retrieving property {"+xpath+"}. Not a repeating scalar?"
739 +pe.getLocalizedMessage());
740 } catch(IndexOutOfBoundsException ioobe) {
741 // Nuxeo sometimes handles missing sub, and sometimes does not. Odd.
742 return ""; // gracefully handle missing elements
743 } catch(ClassCastException cce) {
744 throw new RuntimeException("Problem retrieving property {"+xpath+"} as String. Not a repeating String property?"
745 +cce.getLocalizedMessage());
746 } catch(Exception e) {
747 throw new RuntimeException("Unknown problem retrieving property {"+xpath+"}."
748 +e.getLocalizedMessage());
754 * Gets first of a repeating list of scalar values, as a String, from the document.
756 * @param docModel The document model to get info from
757 * @param schema The name of the schema (part)
758 * @param listName The name of the scalar list property
759 * @return first value in list, as a String, or empty string if the list is empty
761 protected String getStringValueInPrimaryRepeatingComplexProperty(
762 DocumentModel docModel, String schema, String complexPropertyName, String fieldName) {
763 String result = null;
765 String xpath = "/" + NuxeoUtils.getPrimaryXPathPropertyName(schema, complexPropertyName, fieldName);
767 result = (String)docModel.getPropertyValue(xpath);
768 } catch(PropertyException pe) {
769 throw new RuntimeException("Problem retrieving property {"+xpath+"}. Bad propertyNames?"
770 +pe.getLocalizedMessage());
771 } catch(IndexOutOfBoundsException ioobe) {
772 // Nuxeo sometimes handles missing sub, and sometimes does not. Odd.
773 result = ""; // gracefully handle missing elements
774 } catch(ClassCastException cce) {
775 throw new RuntimeException("Problem retrieving property {"+xpath+"} as String. Not a String property?"
776 +cce.getLocalizedMessage());
777 } catch(Exception e) {
778 throw new RuntimeException("Unknown problem retrieving property {"+xpath+"}."
779 +e.getLocalizedMessage());
786 * Gets XPath value from schema. Note that only "/" and "[n]" are
787 * supported for xpath. Can omit grouping elements for repeating complex types,
788 * e.g., "fieldList/[0]" can be used as shorthand for "fieldList/field[0]" and
789 * "fieldGroupList/[0]/field" can be used as shorthand for "fieldGroupList/fieldGroup[0]/field".
790 * If there are no entries for a list of scalars or for a list of complex types,
791 * a 0 index expression (e.g., "fieldGroupList/[0]/field") will safely return an empty
792 * string. A non-zero index will throw an IndexOutOfBoundsException if there are not
793 * that many elements in the list.
794 * N.B.: This does not follow the XPath spec - indices are 0-based, not 1-based.
796 * @param docModel The document model to get info from
797 * @param schema The name of the schema (part)
798 * @param xpath The XPath expression (without schema prefix)
799 * @return value the indicated property value as a String
801 protected Object getListResultValue(DocumentModel docModel, // REM - CSPACE-5133
802 String schema, ListResultField field) {
803 Object result = null;
805 result = NuxeoUtils.getXPathValue(docModel, schema, field.getXpath());
811 protected void removeFromList(List<RelationsCommonList.RelationListItem> list, RelationsCommonList.RelationListItem item) {
815 private void itemToString(StringBuilder sb, String prefix, RelationsCommonList.RelationListItem item ) {
817 sb.append((item.getCsid()!= null)?item.getCsid():"NO CSID");
819 sb.append((item.getSubject().getCsid()!=null)?item.getSubject().getCsid():item.getSubject().getRefName());
821 sb.append(item.getPredicate());
823 sb.append((item.getObject().getCsid()!=null)?item.getObject().getCsid():item.getObject().getRefName());
827 private String dumpList(List<RelationsCommonList.RelationListItem> list, String label) {
828 StringBuilder sb = new StringBuilder();
830 if (list.size() > 0) {
831 sb.append("=========== " + label + " ==========" + CR);
833 for (RelationsCommonList.RelationListItem item : list) {
834 itemToString(sb, "== ", item);
837 return sb.toString();
840 /** @return null on parent not found
842 protected String getParentCSID(String thisCSID) throws Exception {
843 String parentCSID = null;
845 String predicate = RelationshipType.HAS_BROADER.value();
846 RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
847 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
848 if (parentList != null) {
849 if (parentList.size() == 0) {
852 RelationsCommonList.RelationListItem relationListItem = parentList.get(0);
853 parentCSID = relationListItem.getObjectCsid();
856 } catch (Exception e) {
857 logger.error("Could not find parent for this: " + thisCSID, e);
862 protected List<RelationsCommonList.RelationListItem> newRelationsCommonList() {
863 List<RelationsCommonList.RelationListItem> result = new ArrayList<RelationsCommonList.RelationListItem>();
867 public void showSiblings(DocumentWrapper<DocumentModel> wrapDoc,
868 MultipartServiceContext ctx) throws Exception {
869 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
870 String parentCSID = getParentCSID(thisCSID);
871 if (parentCSID == null) {
872 logger.warn("~~~~~\r\n~~~~ Could not find parent for this: " + thisCSID);
876 String predicate = RelationshipType.HAS_BROADER.value();
877 RelationsCommonList siblingListOuter = getRelations(null, parentCSID, predicate);
878 List<RelationsCommonList.RelationListItem> siblingList = siblingListOuter.getRelationListItem();
880 List<RelationsCommonList.RelationListItem> toRemoveList = newRelationsCommonList();
883 RelationsCommonList.RelationListItem item = null;
884 for (RelationsCommonList.RelationListItem sibling : siblingList) {
885 if (thisCSID.equals(sibling.getSubjectCsid())) {
886 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.
889 //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.
890 for (RelationsCommonList.RelationListItem self : toRemoveList) {
891 removeFromList(siblingList, self);
894 long siblingSize = siblingList.size();
895 siblingListOuter.setTotalItems(siblingSize);
896 siblingListOuter.setItemsInPage(siblingSize);
897 if(logger.isTraceEnabled()) {
898 String dump = dumpList(siblingList, "Siblings of: "+thisCSID);
899 logger.trace("~~~~~~~~~~~~~~~~~~~~~~ showSiblings ~~~~~~~~~~~~~~~~~~~~~~~~" + CR + dump);
902 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, siblingListOuter);
903 ctx.addOutputPart(relationsPart);
906 public void showRelations(DocumentWrapper<DocumentModel> wrapDoc,
907 MultipartServiceContext ctx) throws Exception {
908 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
910 String predicate = RelationshipType.HAS_BROADER.value();
911 RelationsCommonList parentListOuter = getRelations(thisCSID, null, predicate);
912 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
914 RelationsCommonList childrenListOuter = getRelations(null, thisCSID, predicate);
915 List<RelationsCommonList.RelationListItem> childrenList = childrenListOuter.getRelationListItem();
917 if(logger.isTraceEnabled()) {
918 String dump = dumpLists(thisCSID, parentList, childrenList, null);
919 logger.trace("~~~~~~~~~~~~~~~~~~~~~~ showRelations ~~~~~~~~~~~~~~~~~~~~~~~~" + CR + dump);
922 //Assume that there are more children than parents. Will be true for parent/child, but maybe not for other relations.
923 //Now add all parents to our childrenList, to be able to return just one list of consolidated results.
924 //Not optimal, but that's the current design spec.
926 for (RelationsCommonList.RelationListItem parent : parentList) {
927 childrenList.add(parent);
930 long childrenSize = childrenList.size();
931 childrenListOuter.setTotalItems(childrenSize);
932 childrenListOuter.setItemsInPage(childrenListOuter.getItemsInPage() + added);
934 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, childrenListOuter);
935 ctx.addOutputPart(relationsPart);
938 public void showAllRelations(DocumentWrapper<DocumentModel> wrapDoc, MultipartServiceContext ctx) throws Exception {
939 String thisCSID = NuxeoUtils.getCsid(wrapDoc.getWrappedObject());
941 RelationsCommonList subjectListOuter = getRelations(thisCSID, null, null); // nulls are wildcards: predicate=*, and object=*
942 List<RelationsCommonList.RelationListItem> subjectList = subjectListOuter.getRelationListItem();
944 RelationsCommonList objectListOuter = getRelations(null, thisCSID, null); // nulls are wildcards: subject=*, and predicate=*
945 List<RelationsCommonList.RelationListItem> objectList = objectListOuter.getRelationListItem();
947 if(logger.isTraceEnabled()) {
948 String dump = dumpLists(thisCSID, subjectList, objectList, null);
949 logger.trace("~~~~~~~~~~~~~~~~~~~~~~ showAllRelations ~~~~~~~~~~~~~~~~~~~~~~~~" + CR + dump);
952 subjectList.addAll(objectList);
954 //now subjectList actually has records BOTH where thisCSID is subject and object.
955 long relatedSize = subjectList.size();
956 subjectListOuter.setTotalItems(relatedSize);
957 subjectListOuter.setItemsInPage(relatedSize);
959 PayloadOutputPart relationsPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, subjectListOuter);
960 ctx.addOutputPart(relationsPart);
963 private String dumpLists(String itemCSID,
964 List<RelationsCommonList.RelationListItem> parentList,
965 List<RelationsCommonList.RelationListItem> childList,
966 List<RelationsCommonList.RelationListItem> actionList) {
967 StringBuilder sb = new StringBuilder();
968 sb.append("itemCSID: " + itemCSID + CR);
969 if(parentList!=null) {
970 sb.append(dumpList(parentList, "parentList"));
972 if(childList!=null) {
973 sb.append(dumpList(childList, "childList"));
975 if(actionList!=null) {
976 sb.append(dumpList(actionList, "actionList"));
978 return sb.toString();
981 //================= TODO: move this to common, refactoring this and CollectionObjectResource.java
982 public RelationsCommonList getRelations(String subjectCSID, String objectCSID, String predicate) throws Exception {
983 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
984 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
985 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
986 queryParams.putSingle(IRelationsManager.SUBJECT_QP, subjectCSID);
987 queryParams.putSingle(IRelationsManager.OBJECT_QP, objectCSID);
989 RelationResource relationResource = new RelationResource(); //is this still acting like a singleton as it should be?
990 RelationsCommonList relationsCommonList = relationResource.getList(ctx);
991 return relationsCommonList;
993 //============================= END TODO refactor ==========================
995 // this method calls the RelationResource to have it create the relations and persist them.
996 private void createRelations(List<RelationsCommonList.RelationListItem> inboundList,
997 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) throws Exception {
998 for (RelationsCommonList.RelationListItem item : inboundList) {
999 RelationsCommon rc = new RelationsCommon();
1000 //rc.setCsid(item.getCsid());
1001 //todo: assignTo(item, rc);
1002 RelationsDocListItem itemSubject = item.getSubject();
1003 RelationsDocListItem itemObject = item.getObject();
1005 // Set at least one of CSID and refName for Subject and Object
1006 // Either value might be null for for each of Subject and Object
1007 String subjectCsid = itemSubject.getCsid();
1008 rc.setSubjectCsid(subjectCsid);
1010 String objCsid = itemObject.getCsid();
1011 rc.setObjectCsid(objCsid);
1013 rc.setSubjectRefName(itemSubject.getRefName());
1014 rc.setObjectRefName(itemObject.getRefName());
1016 rc.setRelationshipType(item.getPredicate());
1017 //RelationshipType foo = (RelationshipType.valueOf(item.getPredicate())) ;
1018 //rc.setPredicate(foo); //this must be one of the type found in the enum in services/jaxb/src/main/resources/relations_common.xsd
1020 // This is superfluous, since it will be fetched by the Relations Create logic.
1021 rc.setSubjectDocumentType(itemSubject.getDocumentType());
1022 rc.setObjectDocumentType(itemObject.getDocumentType());
1024 // This is superfluous, since it will be fetched by the Relations Create logic.
1025 rc.setSubjectUri(itemSubject.getUri());
1026 rc.setObjectUri(itemObject.getUri());
1027 // May not have the info here. Only really require CSID or refName.
1028 // Rest is handled in the Relation create mechanism
1029 //uriPointsToSameAuthority(itemSubject.getUri(), itemObject.getUri());
1031 PoxPayloadOut payloadOut = new PoxPayloadOut(RelationClient.SERVICE_PAYLOAD_NAME);
1032 PayloadOutputPart outputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMONPART_NAME, rc);
1033 payloadOut.addPart(outputPart);
1034 RelationResource relationResource = new RelationResource();
1035 Response res = relationResource.create(ctx, ctx.getResourceMap(),
1036 ctx.getUriInfo(), payloadOut.toXML()); //NOTE ui recycled from above to pass in unknown query params.
1040 // Note that item2 may be sparse (only refName, no CSID for subject or object)
1041 // But item1 must not be sparse
1042 private boolean itemsEqual(RelationsCommonList.RelationListItem item1, RelationsCommonList.RelationListItem item2) {
1043 if (item1 == null || item2 == null) {
1046 RelationsDocListItem subj1 = item1.getSubject();
1047 RelationsDocListItem subj2 = item2.getSubject();
1048 RelationsDocListItem obj1 = item1.getObject();
1049 RelationsDocListItem obj2 = item2.getObject();
1050 String subj1Csid = subj1.getCsid();
1051 String subj2Csid = subj2.getCsid();
1052 String subj1RefName = subj1.getRefName();
1053 String subj2RefName = subj2.getRefName();
1055 String obj1Csid = obj1.getCsid();
1056 String obj2Csid = obj2.getCsid();
1057 String obj1RefName = obj1.getRefName();
1058 String obj2RefName = obj2.getRefName();
1061 (subj1Csid.equals(subj2Csid) || ((subj2Csid==null) && subj1RefName.equals(subj2RefName)))
1062 && (obj1Csid.equals(obj1Csid) || ((obj2Csid==null) && obj1RefName.equals(obj2RefName)))
1063 // predicate is proper, but still allow relationshipType
1064 && (item1.getPredicate().equals(item2.getPredicate())
1065 || ((item2.getPredicate()==null) && item1.getRelationshipType().equals(item2.getRelationshipType())))
1066 // Allow missing docTypes, so long as they do not conflict
1067 && (obj1.getDocumentType().equals(obj2.getDocumentType()) || obj2.getDocumentType()==null)
1068 && (subj1.getDocumentType().equals(subj2.getDocumentType()) || subj2.getDocumentType()==null);
1072 // Note that the item argument may be sparse (only refName, no CSID for subject or object)
1073 // But the list items must not be sparse
1074 private RelationsCommonList.RelationListItem findInList(
1075 List<RelationsCommonList.RelationListItem> list,
1076 RelationsCommonList.RelationListItem item) {
1077 RelationsCommonList.RelationListItem foundItem = null;
1078 for (RelationsCommonList.RelationListItem listItem : list) {
1079 if (itemsEqual(listItem, item)) { //equals must be defined, else
1080 foundItem = listItem;
1087 /** updateRelations strategy:
1090 go through inboundList, remove anything from childList that matches from childList
1091 go through inboundList, remove anything from parentList that matches from parentList
1092 go through parentList, delete all remaining
1093 go through childList, delete all remaining
1094 go through actionList, add all remaining.
1095 check for duplicate children
1096 check for more than one parent.
1098 inboundList parentList childList actionList
1099 ---------------- --------------- ---------------- ----------------
1100 child-a parent-c child-a child-b
1101 child-b parent-d child-c
1106 private RelationsCommonList updateRelations(
1107 String itemCSID, PoxPayloadIn input, DocumentWrapper<DocumentModel> wrapDoc, boolean fUpdate)
1109 if (logger.isTraceEnabled()) {
1110 logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID);
1112 PayloadInputPart part = input.getPart(RelationClient.SERVICE_COMMON_LIST_NAME); //input.getPart("relations_common");
1114 return null; //nothing to do--they didn't send a list of relations.
1116 RelationsCommonList relationsCommonListBody = (RelationsCommonList) part.getBody();
1117 List<RelationsCommonList.RelationListItem> inboundList = relationsCommonListBody.getRelationListItem();
1118 List<RelationsCommonList.RelationListItem> actionList = newRelationsCommonList();
1119 List<RelationsCommonList.RelationListItem> childList = null;
1120 List<RelationsCommonList.RelationListItem> parentList = null;
1121 DocumentModel docModel = wrapDoc.getWrappedObject();
1122 String itemRefName = (String) docModel.getPropertyValue(AuthorityItemJAXBSchema.REF_NAME);
1124 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
1125 //Do magic replacement of ${itemCSID} and fix URI's.
1126 fixupInboundListItems(ctx, inboundList, docModel, itemCSID);
1128 String HAS_BROADER = RelationshipType.HAS_BROADER.value();
1129 UriInfo uriInfo = ctx.getUriInfo();
1130 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
1133 //Run getList() once as sent to get childListOuter:
1134 String predicate = RelationshipType.HAS_BROADER.value();
1135 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
1136 queryParams.putSingle(IRelationsManager.SUBJECT_QP, null);
1137 queryParams.putSingle(IRelationsManager.SUBJECT_TYPE_QP, null);
1138 queryParams.putSingle(IRelationsManager.OBJECT_QP, itemCSID);
1139 queryParams.putSingle(IRelationsManager.OBJECT_TYPE_QP, null);
1141 RelationResource relationResource = new RelationResource();
1142 RelationsCommonList childListOuter = relationResource.getList(ctx); // Knows all query params because they are in the context.
1144 //Now run getList() again, leaving predicate, swapping subject and object, to get parentListOuter.
1145 queryParams.putSingle(IRelationsManager.PREDICATE_QP, predicate);
1146 queryParams.putSingle(IRelationsManager.SUBJECT_QP, itemCSID);
1147 queryParams.putSingle(IRelationsManager.OBJECT_QP, null);
1148 RelationsCommonList parentListOuter = relationResource.getList(ctx);
1151 childList = childListOuter.getRelationListItem();
1152 parentList = parentListOuter.getRelationListItem();
1154 if (parentList.size() > 1) {
1155 throw new Exception("Too many parents for object: " + itemCSID + " list: " + dumpList(parentList, "parentList"));
1158 if (logger.isTraceEnabled()) {
1159 logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID + " got existing relations.");
1163 for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
1164 // Note that the relations may specify the other (non-item) bit with a refName, not a CSID,
1165 // and so the CSID for those may be null
1166 if(inboundItem.getPredicate().equals(HAS_BROADER)) {
1167 // Look for parents and children
1168 if(itemCSID.equals(inboundItem.getObject().getCsid())
1169 || itemRefName.equals(inboundItem.getObject().getRefName())) {
1170 //then this is an item that says we have a child. That child is inboundItem
1171 RelationsCommonList.RelationListItem childItem =
1172 (childList == null) ? null : findInList(childList, inboundItem);
1173 if (childItem != null) {
1174 if (logger.isTraceEnabled()) {
1175 StringBuilder sb = new StringBuilder();
1176 itemToString(sb, "== Child: ", childItem);
1177 logger.trace("Found inboundChild in current child list: " + sb.toString());
1179 removeFromList(childList, childItem); //exists, just take it off delete list
1181 if (logger.isTraceEnabled()) {
1182 StringBuilder sb = new StringBuilder();
1183 itemToString(sb, "== Child: ", inboundItem);
1184 logger.trace("inboundChild not in current child list, will add: " + sb.toString());
1186 actionList.add(inboundItem); //doesn't exist as a child, but is a child. Add to additions list
1187 String newChildCsid = inboundItem.getSubject().getCsid();
1188 if(newChildCsid == null) {
1189 String newChildRefName = inboundItem.getSubject().getRefName();
1190 if(newChildRefName==null) {
1191 throw new RuntimeException("Child with no CSID or refName!");
1193 if (logger.isTraceEnabled()) {
1194 logger.trace("Fetching CSID for child with only refname: "+newChildRefName);
1196 DocumentModel newChildDocModel =
1197 ResourceBase.getDocModelForRefName(this.getRepositorySession(),
1198 newChildRefName, getServiceContext().getResourceMap());
1199 newChildCsid = getCsid(newChildDocModel);
1201 ensureChildHasNoOtherParents(ctx, queryParams, newChildCsid);
1204 } else if (itemCSID.equals(inboundItem.getSubject().getCsid())
1205 || itemRefName.equals(inboundItem.getSubject().getRefName())) {
1206 //then this is an item that says we have a parent. inboundItem is that parent.
1207 RelationsCommonList.RelationListItem parentItem =
1208 (parentList == null) ? null : findInList(parentList, inboundItem);
1209 if (parentItem != null) {
1210 removeFromList(parentList, parentItem); //exists, just take it off delete list
1212 actionList.add(inboundItem); //doesn't exist as a parent, but is a parent. Add to additions list
1215 logger.error("Parent/Child Element didn't link to this item. inboundItem: " + inboundItem);
1218 logger.warn("Non-parent relation ignored. inboundItem: " + inboundItem);
1221 if (logger.isTraceEnabled()) {
1222 String dump = dumpLists(itemCSID, parentList, childList, actionList);
1223 logger.trace("~~~~~~~~~~~~~~~~~~~~~~dump~~~~~~~~~~~~~~~~~~~~~~~~" + CR + dump);
1226 if (logger.isTraceEnabled()) {
1227 logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID + " deleting "
1228 + parentList.size() + " existing parents and " + childList.size() + " existing children.");
1230 deleteRelations(parentList, ctx, "parentList"); //todo: there are items appearing on both lists....april 20.
1231 deleteRelations(childList, ctx, "childList");
1233 if (logger.isTraceEnabled()) {
1234 logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID + " adding "
1235 + actionList.size() + " new parents and children.");
1237 createRelations(actionList, ctx);
1238 if (logger.isTraceEnabled()) {
1239 logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID + " done.");
1241 //We return all elements on the inbound list, since we have just worked to make them exist in the system
1242 // and be non-redundant, etc. That list came from relationsCommonListBody, so it is still attached to it, just pass that back.
1243 return relationsCommonListBody;
1246 /** Performs substitution for ${itemCSID} (see CommonAPI.AuthorityItemCSID_REPLACE for constant)
1247 * and sets URI correctly for related items.
1248 * Operates directly on the items in the list. Does not change the list ordering, does not add or remove any items.
1250 protected void fixupInboundListItems(ServiceContext ctx,
1251 List<RelationsCommonList.RelationListItem> inboundList,
1252 DocumentModel docModel,
1253 String itemCSID) throws Exception {
1254 String thisURI = this.getUri(docModel);
1255 // WARNING: the two code blocks below are almost identical and seem to ask to be put in a generic method.
1256 // beware of the little diffs in inboundItem.setObjectCsid(itemCSID); and inboundItem.setSubjectCsid(itemCSID); in the two blocks.
1257 for (RelationsCommonList.RelationListItem inboundItem : inboundList) {
1258 RelationsDocListItem inboundItemObject = inboundItem.getObject();
1259 RelationsDocListItem inboundItemSubject = inboundItem.getSubject();
1261 if (CommonAPI.AuthorityItemCSID_REPLACE.equalsIgnoreCase(inboundItemObject.getCsid())) {
1262 inboundItem.setObjectCsid(itemCSID);
1263 inboundItemObject.setCsid(itemCSID);
1264 //inboundItemObject.setUri(getUri(docModel));
1267 String objectCsid = inboundItemObject.getCsid();
1268 DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, objectCsid); //null if not found.
1269 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
1270 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
1271 inboundItemObject.setUri(uri); //CSPACE-4037
1274 //uriPointsToSameAuthority(thisURI, inboundItemObject.getUri()); //CSPACE-4042
1276 if (CommonAPI.AuthorityItemCSID_REPLACE.equalsIgnoreCase(inboundItemSubject.getCsid())) {
1277 inboundItem.setSubjectCsid(itemCSID);
1278 inboundItemSubject.setCsid(itemCSID);
1279 //inboundItemSubject.setUri(getUri(docModel));
1282 String subjectCsid = inboundItemSubject.getCsid();
1283 DocumentModel itemDocModel = NuxeoUtils.getDocFromCsid(getRepositorySession(), ctx, subjectCsid); //null if not found.
1284 DocumentWrapper wrapper = new DocumentWrapperImpl(itemDocModel);
1285 String uri = this.getRepositoryClient(ctx).getDocURI(wrapper);
1286 inboundItemSubject.setUri(uri); //CSPACE-4037
1289 //uriPointsToSameAuthority(thisURI, inboundItemSubject.getUri()); //CSPACE-4042
1294 private void ensureChildHasNoOtherParents(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1295 MultivaluedMap<String, String> queryParams, String childCSID) {
1296 logger.trace("ensureChildHasNoOtherParents for: " + childCSID );
1297 queryParams.putSingle(IRelationsManager.SUBJECT_QP, childCSID);
1298 queryParams.putSingle(IRelationsManager.PREDICATE_QP, RelationshipType.HAS_BROADER.value());
1299 queryParams.putSingle(IRelationsManager.OBJECT_QP, null); //null means ANY
1301 RelationResource relationResource = new RelationResource();
1302 RelationsCommonList parentListOuter = relationResource.getList(ctx);
1303 List<RelationsCommonList.RelationListItem> parentList = parentListOuter.getRelationListItem();
1304 //logger.warn("ensureChildHasNoOtherParents preparing to delete relations on "+childCSID+"\'s parent list: \r\n"+dumpList(parentList, "duplicate parent list"));
1305 deleteRelations(parentList, ctx, "parentList-delete");
1308 private void deleteRelations(List<RelationsCommonList.RelationListItem> list,
1309 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1312 for (RelationsCommonList.RelationListItem item : list) {
1313 RelationResource relationResource = new RelationResource();
1314 if(logger.isTraceEnabled()) {
1315 StringBuilder sb = new StringBuilder();
1316 itemToString(sb, "==== TO DELETE: ", item);
1317 logger.trace(sb.toString());
1319 Response res = relationResource.deleteWithParentCtx(ctx, item.getCsid());
1320 if (logger.isDebugEnabled()) {
1321 logger.debug("Status of authority item deleteRelations method call was: " + res.getStatus());
1324 } catch (Throwable t) {
1325 String msg = "Unable to deleteRelations: " + Tools.errorToString(t, true);
1330 // Note that we must do this after we have completed the Update, so that the repository has the
1331 // info for the item itself. The relations code must call into the repo to get info for each end.
1332 // This could be optimized to pass in the parent docModel, since it will often be one end.
1333 // Nevertheless, we should complete the item save before we do work on the relations, especially
1334 // since a save on Create might fail, and we would not want to create relations for something
1335 // that may not be created...
1336 private void handleRelationsPayload(DocumentWrapper<DocumentModel> wrapDoc, boolean fUpdate) throws Exception {
1337 ServiceContext ctx = getServiceContext();
1338 PoxPayloadIn input = (PoxPayloadIn) ctx.getInput();
1339 DocumentModel documentModel = (wrapDoc.getWrappedObject());
1340 String itemCsid = documentModel.getName();
1342 //Updates relations part
1343 RelationsCommonList relationsCommonList = updateRelations(itemCsid, input, wrapDoc, fUpdate);
1345 PayloadOutputPart payloadOutputPart = new PayloadOutputPart(RelationClient.SERVICE_COMMON_LIST_NAME, relationsCommonList); //FIXME: REM - We should check for a null relationsCommonList and not create the new common list payload
1346 ctx.setProperty(RelationClient.SERVICE_COMMON_LIST_NAME, payloadOutputPart);
1348 //now we add part for relations list
1349 //ServiceContext ctx = getServiceContext();
1350 //PayloadOutputPart foo = (PayloadOutputPart) ctx.getProperty(RelationClient.SERVICE_COMMON_LIST_NAME);
1351 ((PoxPayloadOut) ctx.getOutput()).addPart(payloadOutputPart);
1355 * Checks to see if the refName has changed, and if so,
1356 * uses utilities to find all references and update them.
1359 protected void handleItemRefNameReferenceUpdate() throws Exception {
1360 if (newRefNameOnUpdate != null && oldRefNameOnUpdate != null) {
1361 // We have work to do.
1362 if (logger.isDebugEnabled()) {
1363 String eol = System.getProperty("line.separator");
1364 logger.debug("Need to find and update references to Item." + eol
1365 + " Old refName" + oldRefNameOnUpdate + eol
1366 + " New refName" + newRefNameOnUpdate);
1368 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
1369 RepositoryClient repoClient = getRepositoryClient(ctx);
1370 String refNameProp = getRefPropName();
1372 int nUpdated = RefNameServiceUtils.updateAuthorityRefDocs(ctx, repoClient, this.getRepositorySession(),
1373 oldRefNameOnUpdate, newRefNameOnUpdate, refNameProp);
1374 if (logger.isDebugEnabled()) {
1375 logger.debug("Updated " + nUpdated + " instances of oldRefName to newRefName");
1381 * Note: The Vocabulary document handler overrides this method.
1383 protected String getRefPropName() {
1384 return ServiceBindingUtils.AUTH_REF_PROP;