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 java.util.ArrayList;
27 import java.util.List;
30 import javax.ws.rs.core.MultivaluedMap;
31 import javax.ws.rs.core.Response;
33 import org.collectionspace.services.client.AbstractCommonListUtils;
34 import org.collectionspace.services.client.AuthorityClient;
35 import org.collectionspace.services.client.PayloadInputPart;
36 import org.collectionspace.services.client.PoxPayloadIn;
37 import org.collectionspace.services.client.PoxPayloadOut;
38 import org.collectionspace.services.client.XmlTools;
39 import org.collectionspace.services.client.workflow.WorkflowClient;
40 import org.collectionspace.services.common.ResourceMap;
41 import org.collectionspace.services.common.api.RefName;
42 import org.collectionspace.services.common.api.RefName.Authority;
43 import org.collectionspace.services.common.api.RefNameUtils;
44 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
45 import org.collectionspace.services.common.api.Tools;
46 import org.collectionspace.services.common.context.ServiceContext;
47 import org.collectionspace.services.common.document.DocumentException;
48 import org.collectionspace.services.common.document.DocumentNotFoundException;
49 import org.collectionspace.services.common.document.DocumentReferenceException;
50 import org.collectionspace.services.common.document.DocumentWrapper;
51 import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
52 import org.collectionspace.services.common.vocabulary.AuthorityJAXBSchema;
53 import org.collectionspace.services.common.vocabulary.AuthorityResource;
54 import org.collectionspace.services.common.vocabulary.AuthorityServiceUtils;
55 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
56 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
57 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
58 import org.collectionspace.services.config.service.ObjectPartType;
59 import org.collectionspace.services.jaxb.AbstractCommonList;
60 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
61 import org.collectionspace.services.lifecycle.TransitionDef;
62 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler;
63 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
64 import org.collectionspace.services.nuxeo.client.java.RepositoryClientImpl;
65 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
67 import org.dom4j.Element;
68 import org.nuxeo.ecm.core.api.ClientException;
69 import org.nuxeo.ecm.core.api.DocumentModel;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
74 * AuthorityDocumentModelHandler
76 * $LastChangedRevision: $
79 @SuppressWarnings("rawtypes")
80 public abstract class AuthorityDocumentModelHandler<AuthCommon>
81 extends NuxeoDocumentModelHandler<AuthCommon> {
83 private final Logger logger = LoggerFactory.getLogger(AuthorityDocumentModelHandler.class);
85 protected String authorityCommonSchemaName;
86 protected String authorityItemCommonSchemaName;
87 protected boolean shouldUpdateRevNumber = true; // default to updating the revision number
89 public AuthorityDocumentModelHandler(String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
90 this.authorityCommonSchemaName = authorityCommonSchemaName;
91 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
94 public void setShouldUpdateRevNumber(boolean flag) {
95 this.shouldUpdateRevNumber = flag;
98 public boolean getShouldUpdateRevNumber() {
99 return this.shouldUpdateRevNumber;
103 * The entity type expected from the JAX-RS Response object
105 public Class<String> getEntityResponseType() {
110 public void prepareSync() throws Exception {
111 this.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev nums on sync operations
114 protected PayloadInputPart extractPart(Response res, String partLabel)
116 PoxPayloadIn input = new PoxPayloadIn((String)res.readEntity(getEntityResponseType()));
117 PayloadInputPart payloadInputPart = input.getPart(partLabel);
118 if (payloadInputPart == null) {
119 logger.error("Part " + partLabel + " was unexpectedly null.");
121 return payloadInputPart;
125 public boolean handleSync(DocumentWrapper<Object> wrapDoc) throws Exception {
126 boolean result = false;
127 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
128 Specifier specifier = (Specifier) wrapDoc.getWrappedObject();
130 // Get the rev number of the authority so we can compare with rev number of shared authority
132 DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, specifier);
133 if (docModel != null) {
134 Long localRev = (Long) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REV);
135 String shortId = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
136 String remoteClientConfigName = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REMOTECLIENT_CONFIG_NAME); // If set, contains the name of the remote client configuration (remoteClientConfigName) from the tenant bindings
138 // Using the short ID of the local authority, create a URN specifier to retrieve the SAS authority
140 Specifier sasSpecifier = new Specifier(SpecifierForm.URN_NAME, shortId);
141 PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(ctx, remoteClientConfigName, sasSpecifier, getEntityResponseType());
143 // If the authority on the SAS is newer, synch all the items and then the authority record as well
146 Long sasRev = getRevision(sasPayloadIn);
147 if (sasRev > localRev) {
149 // First, sync all the authority items
151 syncAllItems(ctx, sasSpecifier);
153 // Next, sync the authority resource/record itself
155 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
156 ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, AuthorityServiceUtils.DONT_UPDATE_REV); // Don't update the rev number, use the rev number for the SAS instance instead
157 PoxPayloadOut payloadOut = authorityResource.update(ctx, ctx.getResourceMap(), ctx.getUriInfo(), docModel.getName(),
159 if (payloadOut != null) {
160 ctx.setOutput(payloadOut);
164 // We may need to transition the authority into a replicated state the first time we sync it.
166 String workflowState = docModel.getCurrentLifeCycleState();
167 if (workflowState.contains(WorkflowClient.WORKFLOWSTATE_REPLICATED) == false) {
168 String authorityCsid = docModel.getName();
169 authorityResource.updateWorkflowWithTransition(ctx, ctx.getUriInfo(), authorityCsid, WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
173 String errMsg = String.format("Authority of type '%s' with identifier '%s' does not exist.",
174 getServiceContext().getServiceName(), specifier.getURNValue());
175 logger.debug(errMsg);
176 throw new DocumentException(errMsg);
183 * Get the list of authority items from the remote shared authority server (SAS) and try
184 * to synchronize them with the local items. If items exist on the remote but not the local, we'll create them.
186 protected int syncAllItems(ServiceContext ctx, Specifier sasAuthoritySpecifier) throws Exception {
190 int alreadySynched = 0;
192 int totalItemsProcessed = 0;
193 ArrayList<String> itemsInRemoteAuthority = new ArrayList<String>();
195 // Iterate over the list of items/terms in the remote authority
197 PoxPayloadIn sasPayloadInItemList = requestPayloadInItemList(ctx, sasAuthoritySpecifier);
198 List<Element> itemList = getItemList(sasPayloadInItemList);
199 if (itemList != null) {
200 for (Element e:itemList) {
201 String remoteRefName = XmlTools.getElementValue(e, AuthorityItemJAXBSchema.REF_NAME);
202 itemsInRemoteAuthority.add(XmlTools.getElementValue(e, AuthorityItemJAXBSchema.SHORT_IDENTIFIER));
203 long status = syncRemoteItemWithLocalItem(ctx, remoteRefName);
206 } else if (status == 0) {
211 totalItemsProcessed++;
215 // Now see if we need to deprecate or delete items that have been hard-deleted from the SAS but still exist
216 // locally. Subtract (remove) the list of remote items from the list of local items to determine which
217 // of the remote items have been hard deleted.
219 ArrayList<String> itemsInLocalAuthority = getItemsInLocalAuthority(ctx, sasAuthoritySpecifier);
220 itemsInLocalAuthority.removeAll(itemsInRemoteAuthority);
221 if (itemsInLocalAuthority.size() > 0) {
222 ArrayList<String> remainingItems = itemsInLocalAuthority; // now a subset of local items that no longer exist on the SAS, so we need to try to delete them (or mark them as deprecated if they still have records referencing them)
224 // We now need to either hard-delete or deprecate the remaining authorities
226 long processed = deleteOrDeprecateItems(ctx, sasAuthoritySpecifier, remainingItems);
227 if (processed != remainingItems.size()) {
228 throw new Exception("Encountered unexpected exception trying to delete or deprecated authority items during synchronization.");
232 // Now that we've sync'd all the items, we need to synchronize the hierarchy relationships
234 for (String itemShortId:itemsInRemoteAuthority) {
235 long status = syncRemoteItemRelationshipsWithLocalItem(ctx, sasAuthoritySpecifier, itemShortId);
238 } else if (status == 0) {
243 totalItemsProcessed++;
246 logger.info(String.format("Total number of items processed during sync: %d", totalItemsProcessed));
247 logger.info(String.format("Number of items synchronized: %d", synched));
248 logger.info(String.format("Number of items created during sync: %d", created));
249 logger.info(String.format("Number not needing synchronization: %d", alreadySynched));
255 * This method should only be used as part of a SAS synch operation.
261 private long deleteOrDeprecateItems(ServiceContext ctx, Specifier authoritySpecifier, ArrayList<String> itemShortIdList) throws Exception {
263 AuthorityItemSpecifier authorityItemSpecificer = null;
265 ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, false); // Don't update the revision number when we delete or deprecate the item
266 for (String itemShortId:itemShortIdList) {
267 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
269 authorityItemSpecificer = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authoritySpecifier.value,
271 authorityResource.deleteAuthorityItem(ctx,
272 authorityItemSpecificer.getParentSpecifier().getURNValue(),
273 authorityItemSpecificer.getItemSpecifier().getURNValue(),
274 AuthorityServiceUtils.DONT_UPDATE_REV); // Since we're sync'ing, we shouldn't update the revision number (obviously this only applies to soft-deletes since hard-deletes destroy the record)
276 } catch (DocumentReferenceException de) {
277 logger.info(String.format("Authority item with '%s' has existing references and cannot be removed during sync.",
278 authorityItemSpecificer), de);
279 boolean marked = AuthorityServiceUtils.markAuthorityItemAsDeprecated(ctx, authorityItemCommonSchemaName,
280 authorityItemSpecificer);
281 if (marked == true) {
284 } catch (Exception e) {
285 logger.warn(String.format("Unable to delete authority item '%s'", authorityItemSpecificer), e);
290 if (logger.isWarnEnabled() == true) {
291 if (result != itemShortIdList.size()) {
292 logger.warn(String.format("Unable to delete or deprecate some authority items during synchronization with SAS. Deleted or deprecated %d of %d. See the services log file for details.",
293 result, itemShortIdList.size()));
301 * Gets the list of SAS related items in the local authority. We exlude items with the "proposed" flags because
302 * we want a list with only SAS created items.
304 * We need to add pagination support to this call!!!
307 * @param authoritySpecifier
311 private ArrayList<String> getItemsInLocalAuthority(ServiceContext ctx, Specifier authoritySpecifier) throws Exception {
312 ArrayList<String> result = new ArrayList<String>();
314 ResourceMap resourceMap = ctx.getResourceMap();
315 String resourceName = ctx.getClient().getServiceName();
316 AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
317 AbstractCommonList acl = authorityResource.getAuthorityItemList(ctx, authoritySpecifier.getURNValue(), ctx.getUriInfo());
319 List<ListItem> listItemList = acl.getListItem();
320 for (ListItem listItem:listItemList) {
321 Boolean proposed = getBooleanValue(listItem, AuthorityItemJAXBSchema.PROPOSED);
322 if (proposed == false) { // exclude "proposed" (i.e., local-only items)
323 result.add(AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.SHORT_IDENTIFIER));
330 private Boolean getBooleanValue(ListItem listItem, String name) {
331 Boolean result = null;
333 String value = AbstractCommonListUtils.ListItemGetElementValue(listItem, name);
335 result = Boolean.valueOf(value);
341 private String getStringValue(ListItem listItem, String name) {
342 return AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME);
346 * This method should only be used during a SAS synchronization request.
349 * @param parentIdentifier - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
350 * @param itemIdentifier - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
353 protected void createLocalItem(ServiceContext ctx, String parentIdentifier, String itemIdentifier, Boolean syncHierarchicalRelationships) throws Exception {
355 // Create a URN short ID specifier for the getting a copy of the remote authority item
357 Specifier authoritySpecifier = Specifier.getSpecifier(parentIdentifier);
358 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier);
359 AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(authoritySpecifier, itemSpecifier);
361 // Get the remote client configuration name
363 DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, authoritySpecifier);
364 String remoteClientConfigName = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REMOTECLIENT_CONFIG_NAME); // If set, contains the name of the remote client configuration (remoteClientConfigName) from the tenant bindings
366 // Get the remote payload
368 PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(sasAuthorityItemSpecifier, remoteClientConfigName,
369 ctx.getServiceName(), getEntityResponseType(), syncHierarchicalRelationships);
370 sasPayloadIn = AuthorityServiceUtils.filterRefnameDomains(ctx, sasPayloadIn); // We need to filter domain name part of any and all refnames in the payload
372 // Using the payload from the remote server, create a local copy of the item
374 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
375 Response response = authorityResource.createAuthorityItemWithParentContext(ctx, authoritySpecifier.getURNValue(),
376 sasPayloadIn, AuthorityServiceUtils.DONT_UPDATE_REV, AuthorityServiceUtils.NOT_PROPOSED, AuthorityServiceUtils.SAS_ITEM);
378 // Check the response for successful POST result
380 if (response.getStatus() != Response.Status.CREATED.getStatusCode()) {
381 throw new DocumentException(String.format("Could not create new authority item '%s' during synchronization of the '%s' authority.",
382 itemIdentifier, parentIdentifier));
385 // Since we're creating an item that was sourced from the replication server, we need to replicate it locally.
387 authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier,
388 WorkflowClient.WORKFLOWTRANSITION_REPLICATE, AuthorityServiceUtils.DONT_UPDATE_REV); // don't update the rev number of the new replicated item (use the rev number of the sourced item)
392 * Try to synchronize a remote item (using its refName) with a local item. If the local doesn't yet
393 * exist, we'll create it.
395 * -1 = sync not needed; i.e., already in sync
397 * 1 = local item was missing so we created it
403 protected long syncRemoteItemWithLocalItem(ServiceContext ctx, String itemRefName) throws Exception {
406 // Using the item refname (with no local CSID), create specifiers that we'll use to find the local versions
408 AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(itemRefName);
409 String parentIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name);
410 String itemIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.name);
412 // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
414 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
415 PoxPayloadOut localItemPayloadOut;
417 localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
418 } catch (DocumentNotFoundException dnf) {
420 // Document not found, means we need to create an item/term that exists only on the SAS
422 logger.info(String.format("Remote item with refname='%s' doesn't exist locally, so we'll create it.", itemRefName));
423 createLocalItem(ctx, parentIdentifier, itemIdentifier, AuthorityClient.DONT_INCLUDE_RELATIONS);
424 return 1; // exit with status of 1 means we created a new authority item
427 // If we get here, we know the item exists both locally and remotely, so we need to synchronize them.
431 PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, false);
432 if (theUpdate != null) {
433 result = 0; // means we needed to sync this item with SAS
434 logger.debug(String.format("Sync'd authority item parent='%s' id='%s with SAS. Updated payload is: \n%s",
435 parentIdentifier, itemIdentifier, theUpdate.getXmlPayload()));
437 } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
439 logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
443 return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
447 * Ensure the local items relationships look the same as the remote items' by synchronizing the hierarchy relationship records
448 * of the SAS item with the local item.
455 protected long syncRemoteItemRelationshipsWithLocalItem(ServiceContext ctx, Specifier authoritySpecifier, String itemShortId) throws Exception {
458 String parentIdentifier = authoritySpecifier.getURNValue();
459 String itemIdentifier = Specifier.createShortIdURNValue(itemShortId);
461 // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
463 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
464 PoxPayloadOut localItemPayloadOut;
466 MultivaluedMap queryParams = ctx.getQueryParams();
467 localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
468 } catch (DocumentNotFoundException dnf) {
470 // Document not found, means we need to create an item/term that exists only on the SAS
472 logger.info(String.format("Remote item with short ID ='%s' doesn't exist locally, so we can't synchronize its relationships.", itemShortId));
476 // If we get here, we know the item exists both locally and remotely, so we need to synchronize the hierarchy relationships.
480 PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, AuthorityClient.INCLUDE_RELATIONS);
481 if (theUpdate != null) {
482 result = 0; // means we needed to sync this item with SAS
483 logger.debug(String.format("Sync'd authority item parent='%s' id='%s with SAS. Updated payload is: \n%s",
484 parentIdentifier, itemIdentifier, theUpdate.getXmlPayload()));
486 } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
488 logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
492 return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
498 * Request an authority item list payload from the SAS server.
505 private PoxPayloadIn requestPayloadInItemList(ServiceContext ctx, Specifier specifier) throws Exception {
506 PoxPayloadIn result = null;
508 AuthorityClient client = (AuthorityClient) ctx.getClient();
509 Response res = client.readItemList(specifier.getURNValue(),
510 null, // partial term string
511 null // keyword string
514 int statusCode = res.getStatus();
516 // Check the status code of the response: does it match
517 // the expected response(s)?
518 if (logger.isDebugEnabled()) {
519 logger.debug(client.getClass().getCanonicalName() + ": status = " + statusCode);
522 result = new PoxPayloadIn((String)res.readEntity(getEntityResponseType())); // Get the entire response!
532 * Non standard injection of CSID into common part, since caller may access through
533 * shortId, and not know the CSID.
534 * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
537 protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
539 Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
541 // Add the CSID to the common part
542 if (partMeta.getLabel().equalsIgnoreCase(authorityCommonSchemaName)) {
543 String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
544 unQObjectProperties.put("csid", csid);
547 return unQObjectProperties;
550 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
551 super.fillAllParts(wrapDoc, action);
553 // Update the record's revision number on both CREATE and UPDATE actions, but not on SYNC
555 if (this.getShouldUpdateRevNumber() == true) { // We won't update rev numbers on synchronization with SAS
556 updateRevNumbers(wrapDoc);
560 protected void updateRevNumbers(DocumentWrapper<DocumentModel> wrapDoc) {
561 DocumentModel documentModel = wrapDoc.getWrappedObject();
562 Long rev = (Long)documentModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV);
568 documentModel.setProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV, rev);
572 * We consider workflow state changes as changes that should bump the revision number
574 * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#handleWorkflowTransition(org.collectionspace.services.common.document.DocumentWrapper, org.collectionspace.services.lifecycle.TransitionDef)
577 public void handleWorkflowTransition(ServiceContext ctx, DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) throws Exception {
578 boolean updateRevNumber = this.getShouldUpdateRevNumber();
579 Boolean contextProperty = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
580 if (contextProperty != null) {
581 updateRevNumber = contextProperty;
584 if (updateRevNumber == true) { // We don't update the rev number of synchronization requests
585 updateRevNumbers(wrapDoc);
590 public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
591 super.handleCreate(wrapDoc);
593 // Uncomment once debugged and App layer is read to integrate
594 // Experimenting with this uncommented now ...
595 handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityCommonSchemaName);
596 updateRefnameForAuthority(wrapDoc, authorityCommonSchemaName);//CSPACE-3178
599 protected String buildWhereForShortId(String name) {
600 return authorityCommonSchemaName
601 + ":" + AuthorityJAXBSchema.SHORT_IDENTIFIER
605 private boolean isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
609 private boolean temp_isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
610 boolean result = true;
612 ServiceContext ctx = this.getServiceContext();
613 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
614 String nxqlWhereClause = buildWhereForShortId(shortIdentifier);
616 DocumentWrapper<DocumentModel> searchResultWrapper = getRepositoryClient(ctx).findDoc(ctx, nxqlWhereClause);
617 if (searchResultWrapper != null) {
619 if (logger.isInfoEnabled() == true) {
620 DocumentModel searchResult = searchResultWrapper.getWrappedObject();
621 String debugMsg = String.format("Could not create a new authority with a short identifier of '%s', because one already exists with the same short identifer: CSID = '%s'",
622 shortIdentifier, searchResult.getName());
623 logger.trace(debugMsg);
626 } catch (DocumentNotFoundException e) {
627 // Not a problem, just means we couldn't find another authority with that short ID
634 * If no short identifier was provided in the input payload,
635 * generate a short identifier from the display name. Either way though,
636 * the short identifier needs to be unique.
638 private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
639 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
640 String displayName = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.DISPLAY_NAME);
641 String shortDisplayName = "";
642 String generateShortIdentifier = null;
643 if (Tools.isEmpty(shortIdentifier)) {
644 generateShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(displayName, shortDisplayName);
645 docModel.setProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER, shortIdentifier);
648 if (isUnique(docModel, schemaName) == false) {
649 String shortId = generateShortIdentifier == null ? shortIdentifier : generateShortIdentifier;
650 String errMsgVerb = generateShortIdentifier == null ? "supplied" : "generated";
651 String errMsg = String.format("The %s short identifier '%s' was not unique, so the new authority could not be created.",
652 errMsgVerb, shortId);
653 throw new DocumentException(errMsg);
658 * Generate a refName for the authority from the short identifier
661 * All refNames for authorities are generated. If a client supplies
662 * a refName, it will be overwritten during create (per this method)
663 * or discarded during update (per filterReadOnlyPropertiesForPart).
665 * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
668 protected void updateRefnameForAuthority(DocumentWrapper<DocumentModel> wrapDoc, String schemaName) throws Exception {
669 DocumentModel docModel = wrapDoc.getWrappedObject();
670 RefName.Authority authority = (Authority) getRefName(getServiceContext(), docModel);
671 String refName = authority.toString();
672 docModel.setProperty(schemaName, AuthorityJAXBSchema.REF_NAME, refName);
676 public RefName.RefNameInterface getRefName(ServiceContext ctx,
677 DocumentModel docModel) {
678 RefName.RefNameInterface refname = null;
681 String shortIdentifier = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
682 String displayName = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.DISPLAY_NAME);
683 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
684 ctx.getServiceName(),
685 null, // Only use shortId form!!!
689 } catch (Exception e) {
690 logger.error(e.getMessage(), e);
697 protected String getRefnameDisplayName(DocumentWrapper<DocumentModel> docWrapper) {
698 String result = null;
700 DocumentModel docModel = docWrapper.getWrappedObject();
701 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
702 RefName.Authority refname = (RefName.Authority)getRefName(ctx, docModel);
703 result = refname.getDisplayName();
708 public String getShortIdentifier(ServiceContext ctx, String authCSID, String schemaName) throws Exception {
709 String shortIdentifier = null;
710 CoreSessionInterface repoSession = null;
711 boolean releaseSession = false;
713 RepositoryClientImpl nuxeoRepoClient = (RepositoryClientImpl)this.getRepositoryClient(ctx);
715 repoSession = nuxeoRepoClient.getRepositorySession(ctx);
716 DocumentWrapper<DocumentModel> wrapDoc = nuxeoRepoClient.getDocFromCsid(ctx, repoSession, authCSID);
717 DocumentModel docModel = wrapDoc.getWrappedObject();
718 if (docModel == null) {
719 throw new DocumentNotFoundException(String.format("Could not find authority resource with CSID='%s'.", authCSID));
721 shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
722 } catch (ClientException ce) {
723 throw new RuntimeException("AuthorityDocHandler Internal Error: cannot get shortId!", ce);
725 if (repoSession != null) {
726 nuxeoRepoClient.releaseRepositorySession(ctx, repoSession);
730 return shortIdentifier;
734 * Filters out selected values supplied in an update request.
736 * @param objectProps the properties filtered out from the update payload
737 * @param partMeta metadata for the object to fill
740 public void filterReadOnlyPropertiesForPart(
741 Map<String, Object> objectProps, ObjectPartType partMeta) {
742 super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
743 String commonPartLabel = getServiceContext().getCommonPartLabel();
744 if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
745 objectProps.remove(AuthorityJAXBSchema.CSID);
746 objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
747 objectProps.remove(AuthorityJAXBSchema.REF_NAME);