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.apache.commons.httpclient.HttpStatus;
34 import org.collectionspace.services.client.AbstractCommonListUtils;
35 import org.collectionspace.services.client.AuthorityClient;
36 import org.collectionspace.services.client.PayloadInputPart;
37 import org.collectionspace.services.client.PoxPayloadIn;
38 import org.collectionspace.services.client.PoxPayloadOut;
39 import org.collectionspace.services.client.XmlTools;
40 import org.collectionspace.services.client.workflow.WorkflowClient;
41 import org.collectionspace.services.common.ResourceMap;
42 import org.collectionspace.services.common.api.RefName;
43 import org.collectionspace.services.common.api.RefName.Authority;
44 import org.collectionspace.services.common.api.RefNameUtils;
45 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
46 import org.collectionspace.services.common.api.Tools;
47 import org.collectionspace.services.common.context.ServiceContext;
48 import org.collectionspace.services.common.document.DocumentException;
49 import org.collectionspace.services.common.document.DocumentNotFoundException;
50 import org.collectionspace.services.common.document.DocumentReferenceException;
51 import org.collectionspace.services.common.document.DocumentWrapper;
52 import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
53 import org.collectionspace.services.common.vocabulary.AuthorityJAXBSchema;
54 import org.collectionspace.services.common.vocabulary.AuthorityResource;
55 import org.collectionspace.services.common.vocabulary.AuthorityServiceUtils;
56 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
57 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
58 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
59 import org.collectionspace.services.config.service.ListResultField;
60 import org.collectionspace.services.config.service.ObjectPartType;
61 import org.collectionspace.services.jaxb.AbstractCommonList;
62 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
63 import org.collectionspace.services.lifecycle.TransitionDef;
64 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler;
65 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
66 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
67 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
68 import org.dom4j.Element;
69 import org.nuxeo.ecm.core.api.ClientException;
70 import org.nuxeo.ecm.core.api.DocumentModel;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
75 * AuthorityDocumentModelHandler
77 * $LastChangedRevision: $
80 @SuppressWarnings("rawtypes")
81 public abstract class AuthorityDocumentModelHandler<AuthCommon>
82 extends NuxeoDocumentModelHandler<AuthCommon> {
84 private final Logger logger = LoggerFactory.getLogger(AuthorityDocumentModelHandler.class);
86 protected String authorityCommonSchemaName;
87 protected String authorityItemCommonSchemaName;
88 protected boolean shouldUpdateRevNumber = true; // default to updating the revision number
90 public AuthorityDocumentModelHandler(String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
91 this.authorityCommonSchemaName = authorityCommonSchemaName;
92 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
95 public void setShouldUpdateRevNumber(boolean flag) {
96 this.shouldUpdateRevNumber = flag;
99 public boolean getShouldUpdateRevNumber() {
100 return this.shouldUpdateRevNumber;
104 * The entity type expected from the JAX-RS Response object
106 public Class<String> getEntityResponseType() {
111 public void prepareSync() throws Exception {
112 this.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev nums on sync operations
115 protected PayloadInputPart extractPart(Response res, String partLabel)
117 PoxPayloadIn input = new PoxPayloadIn((String)res.readEntity(getEntityResponseType()));
118 PayloadInputPart payloadInputPart = input.getPart(partLabel);
119 if (payloadInputPart == null) {
120 logger.error("Part " + partLabel + " was unexpectedly null.");
122 return payloadInputPart;
126 public boolean handleSync(DocumentWrapper<Object> wrapDoc) throws Exception {
127 boolean result = false;
128 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
129 Specifier specifier = (Specifier) wrapDoc.getWrappedObject();
131 // Get the rev number of the authority so we can compare with rev number of shared authority
133 DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, specifier);
134 if (docModel != null) {
135 Long localRev = (Long) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REV);
136 String shortId = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
137 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
139 // Using the short ID of the local authority, create a URN specifier to retrieve the SAS authority
141 Specifier sasSpecifier = new Specifier(SpecifierForm.URN_NAME, shortId);
142 PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(ctx, remoteClientConfigName, sasSpecifier, getEntityResponseType());
144 // If the authority on the SAS is newer, synch all the items and then the authority record as well
147 Long sasRev = getRevision(sasPayloadIn);
148 if (sasRev > localRev || true) { // FIXME: Along with the revision number, we need to use other meta information to determine if a sync should happen -for now, alway sync
150 // First, sync all the authority items
152 syncAllItems(ctx, sasSpecifier); // FIXME: We probably want to consider "paging" this instead of handling the entire set of items.
154 // Next, sync the authority resource/record itself
156 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
157 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
158 PoxPayloadOut payloadOut = authorityResource.update(ctx, ctx.getResourceMap(), ctx.getUriInfo(), docModel.getName(),
160 if (payloadOut != null) {
161 ctx.setOutput(payloadOut);
165 // We may need to transition the authority into a replicated state the first time we sync it.
167 String workflowState = docModel.getCurrentLifeCycleState();
168 if (workflowState.contains(WorkflowClient.WORKFLOWSTATE_REPLICATED) == false) {
169 String authorityCsid = docModel.getName();
170 authorityResource.updateWorkflowWithTransition(ctx, ctx.getUriInfo(), authorityCsid, WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
174 String errMsg = String.format("Authority of type '%s' with identifier '%s' does not exist.",
175 getServiceContext().getServiceName(), specifier.getURNValue());
176 logger.debug(errMsg);
177 throw new DocumentException(errMsg);
184 * Get the list of authority items from the remote shared authority server (SAS) and try
185 * to synchronize them with the local items. If items exist on the remote but not the local, we'll create them.
187 protected int syncAllItems(ServiceContext ctx, Specifier sasAuthoritySpecifier) throws Exception {
191 int alreadySynched = 0;
193 int totalItemsProcessed = 0;
194 ArrayList<String> itemsInRemoteAuthority = new ArrayList<String>();
196 // Iterate over the list of items/terms in the remote authority
198 PoxPayloadIn itemListPayload = requestItemList(ctx, sasAuthoritySpecifier);
199 List<Element> itemList = getItemList(itemListPayload);
200 if (itemList != null) {
201 for (Element e:itemList) {
202 String remoteRefName = XmlTools.getElementValue(e, AuthorityItemJAXBSchema.REF_NAME);
203 itemsInRemoteAuthority.add(XmlTools.getElementValue(e, AuthorityItemJAXBSchema.SHORT_IDENTIFIER));
204 long status = syncRemoteItemWithLocalItem(ctx, remoteRefName);
207 } else if (status == 0) {
212 totalItemsProcessed++;
216 // Now see if we need to deprecate or delete items that have been hard-deleted from the SAS but still exist
217 // locally. Subtract (remove) the list of remote items from the list of local items to determine which
218 // of the remote items have been hard deleted.
220 ArrayList<String> itemsInLocalAuthority = getItemsInLocalAuthority(ctx, sasAuthoritySpecifier);
221 itemsInLocalAuthority.removeAll(itemsInRemoteAuthority);
222 if (itemsInLocalAuthority.size() > 0) {
223 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)
225 // We now need to either hard-delete or deprecate the remaining authorities
227 long processed = deleteOrDeprecateItems(ctx, sasAuthoritySpecifier, remainingItems);
228 if (processed != remainingItems.size()) {
229 throw new Exception("Encountered unexpected exception trying to delete or deprecated authority items during synchronization.");
233 // Now that we've sync'd all the items, we need to synchronize the hierarchy relationships
235 for (String itemShortId:itemsInRemoteAuthority) {
236 long status = syncRemoteItemRelationshipsWithLocalItem(ctx, sasAuthoritySpecifier, itemShortId);
239 } else if (status == 0) {
244 totalItemsProcessed++;
247 logger.info(String.format("Total number of items processed during sync: %d", totalItemsProcessed));
248 logger.info(String.format("Number of items synchronized: %d", synched));
249 logger.info(String.format("Number of items created during sync: %d", created));
250 logger.info(String.format("Number not needing synchronization: %d", alreadySynched));
256 * This method should ***only*** be used as part of a SAS synch operation.
263 private long deleteOrDeprecateItems(ServiceContext ctx, Specifier authoritySpecifier, ArrayList<String> itemShortIdList) throws Exception {
265 AuthorityItemSpecifier authorityItemSpecificer = null;
267 ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, false); // Don't update the revision number when we delete or deprecate the item
268 for (String itemShortId:itemShortIdList) {
269 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
271 authorityItemSpecificer = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authoritySpecifier.value,
273 authorityResource.deleteAuthorityItem(ctx,
274 authorityItemSpecificer.getParentSpecifier().getURNValue(),
275 authorityItemSpecificer.getItemSpecifier().getURNValue(),
276 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)
278 } catch (DocumentReferenceException de) {
279 logger.info(String.format("Authority item with '%s' has existing references and cannot be removed during sync.",
280 authorityItemSpecificer), de);
281 boolean marked = AuthorityServiceUtils.markAuthorityItemAsDeprecated(ctx, authorityItemCommonSchemaName,
282 authorityItemSpecificer);
283 if (marked == true) {
286 } catch (Exception e) {
287 logger.warn(String.format("Unable to delete authority item '%s'", authorityItemSpecificer), e);
292 if (logger.isWarnEnabled() == true) {
293 if (result != itemShortIdList.size()) {
294 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.",
295 result, itemShortIdList.size()));
303 * Gets the list of SAS related items in the local authority. We exlude items with the "proposed" flags because
304 * we want a list with only SAS created items.
306 * We need to add pagination support to this call!!!
309 * @param authoritySpecifier
313 private ArrayList<String> getItemsInLocalAuthority(ServiceContext ctx, Specifier authoritySpecifier) throws Exception {
314 ArrayList<String> result = new ArrayList<String>();
316 ResourceMap resourceMap = ctx.getResourceMap();
317 String resourceName = ctx.getClient().getServiceName();
318 AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
319 AbstractCommonList acl = authorityResource.getAuthorityItemList(ctx, authoritySpecifier.getURNValue(), ctx.getUriInfo());
321 List<ListItem> listItemList = acl.getListItem();
322 for (ListItem listItem:listItemList) {
323 Boolean proposed = getBooleanValue(listItem, AuthorityItemJAXBSchema.PROPOSED);
324 if (proposed == false) { // exclude "proposed" (i.e., local-only items)
325 result.add(AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.SHORT_IDENTIFIER));
332 private Boolean getBooleanValue(ListItem listItem, String name) {
333 Boolean result = null;
335 String value = AbstractCommonListUtils.ListItemGetElementValue(listItem, name);
337 result = Boolean.valueOf(value);
343 private String getStringValue(ListItem listItem, String name) {
344 return AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME);
348 * This method should only be used during a SAS synchronization request.
351 * @param parentIdentifier - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
352 * @param itemIdentifier - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
355 protected void createLocalItem(ServiceContext ctx, String parentIdentifier, String itemIdentifier, Boolean syncHierarchicalRelationships) throws Exception {
357 // Create a URN short ID specifier for the getting a copy of the remote authority item
359 Specifier authoritySpecifier = Specifier.getSpecifier(parentIdentifier);
360 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier);
361 AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(authoritySpecifier, itemSpecifier);
363 // Get the remote client configuration name
365 DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, authoritySpecifier);
366 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
368 // Get the remote payload
370 PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(sasAuthorityItemSpecifier, remoteClientConfigName,
371 ctx.getServiceName(), getEntityResponseType(), syncHierarchicalRelationships);
372 sasPayloadIn = AuthorityServiceUtils.localizeRefNameDomains(ctx, sasPayloadIn); // We need to filter domain name part of any and all refnames in the payload
374 // Using the payload from the remote server, create a local copy of the item
376 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
377 Response response = authorityResource.createAuthorityItemWithParentContext(ctx, authoritySpecifier.getURNValue(),
378 sasPayloadIn, AuthorityServiceUtils.DONT_UPDATE_REV, AuthorityServiceUtils.NOT_PROPOSED, AuthorityServiceUtils.SAS_ITEM);
380 // Check the response for successful POST result
382 if (response.getStatus() != Response.Status.CREATED.getStatusCode()) {
383 throw new DocumentException(String.format("Could not create new authority item '%s' during synchronization of the '%s' authority.",
384 itemIdentifier, parentIdentifier));
387 // Since we're creating an item that was sourced from the replication server, we need to replicate it locally.
389 authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier,
390 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)
394 * Try to synchronize a remote item (using its refName) with a local item. If the local doesn't yet
395 * exist, we'll create it.
397 * -1 = sync not needed; i.e., already in sync
399 * 1 = local item was missing so we created it
405 protected long syncRemoteItemWithLocalItem(ServiceContext ctx, String itemRefName) throws Exception {
408 // Using the item refname (with no local CSID), create specifiers that we'll use to find the local versions
410 AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(itemRefName);
411 String parentIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name);
412 String itemIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.name);
414 // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
416 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
417 PoxPayloadOut localItemPayloadOut;
419 localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
420 } catch (DocumentNotFoundException dnf) {
422 // Document not found, means we need to create an item/term that exists only on the SAS
424 logger.info(String.format("Remote item with refname='%s' doesn't exist locally, so we'll create it.", itemRefName));
425 createLocalItem(ctx, parentIdentifier, itemIdentifier, AuthorityClient.DONT_INCLUDE_RELATIONS);
426 return 1; // exit with status of 1 means we created a new authority item
429 // If we get here, we know the item exists both locally and remotely, so we need to synchronize them.
433 PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, false);
434 if (theUpdate != null) {
435 result = 0; // means we needed to sync this item with SAS
436 logger.debug(String.format("Synced authority item %s in authority %s",
437 itemIdentifier, parentIdentifier));
439 } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
441 logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
445 return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
449 * Ensure the local items relationships look the same as the remote items' by synchronizing the hierarchy relationship records
450 * of the SAS item with the local item.
457 protected long syncRemoteItemRelationshipsWithLocalItem(ServiceContext ctx, Specifier authoritySpecifier, String itemShortId) throws Exception {
460 String parentIdentifier = authoritySpecifier.getURNValue();
461 String itemIdentifier = Specifier.createShortIdURNValue(itemShortId);
463 // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
465 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
466 PoxPayloadOut localItemPayloadOut;
468 MultivaluedMap queryParams = ctx.getQueryParams();
469 localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
470 } catch (DocumentNotFoundException dnf) {
472 // Document not found, means we need to create an item/term that exists only on the SAS
474 logger.info(String.format("Remote item with short ID ='%s' doesn't exist locally, so we can't synchronize its relationships.", itemShortId));
478 // If we get here, we know the item exists both locally and remotely, so we need to synchronize the hierarchy relationships.
482 PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, AuthorityClient.INCLUDE_RELATIONS);
483 if (theUpdate != null) {
484 result = 0; // means we needed to sync this item with SAS
485 logger.debug(String.format("Synced authority item %s in authority %s",
486 itemIdentifier, parentIdentifier));
488 } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
490 logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
494 return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
499 private void assertStatusCode(Response res, Specifier specifier, AuthorityClient client) throws Exception {
500 int statusCode = res.getStatus();
502 if (statusCode != HttpStatus.SC_OK) {
503 String errMsg = String.format("Could not retrieve authority information for '%s' on remote server '%s'. Server returned status code %d",
504 specifier.getURNValue(), client.getBaseURL(), statusCode);
505 throw new DocumentException(statusCode, errMsg);
510 * Request an authority item list payload from the SAS server. This is a non-paging solution.
511 * If the authority has a very large number of items/terms, we might not be able to handle them
519 private PoxPayloadIn requestItemList(ServiceContext ctx, Specifier specifier) throws Exception {
520 PoxPayloadIn result = null;
521 AuthorityClient client = (AuthorityClient) ctx.getClient();
523 // Request the item list using the max page size (0).
525 Response res = client.readItemList(
526 specifier.getURNValue(),
527 null, // partial term string
528 null, // keyword string
533 assertStatusCode(res, specifier, client);
536 result = new PoxPayloadIn((String) res.readEntity(getEntityResponseType())); // Get the entire response.
545 * Non standard injection of CSID into common part, since caller may access through
546 * shortId, and not know the CSID.
547 * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
550 protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
552 Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
554 // Add the CSID to the common part
555 if (partMeta.getLabel().equalsIgnoreCase(authorityCommonSchemaName)) {
556 String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
557 unQObjectProperties.put("csid", csid);
560 return unQObjectProperties;
563 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
564 super.fillAllParts(wrapDoc, action);
566 // Update the record's revision number on both CREATE and UPDATE actions, but not on SYNC
568 if (this.getShouldUpdateRevNumber() == true) { // We won't update rev numbers on synchronization with SAS
569 updateRevNumbers(wrapDoc);
573 protected void updateRevNumbers(DocumentWrapper<DocumentModel> wrapDoc) {
574 DocumentModel documentModel = wrapDoc.getWrappedObject();
575 Long rev = (Long)documentModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV);
581 documentModel.setProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV, rev);
585 * We consider workflow state changes as changes that should bump the revision number
587 * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#handleWorkflowTransition(org.collectionspace.services.common.document.DocumentWrapper, org.collectionspace.services.lifecycle.TransitionDef)
590 public void handleWorkflowTransition(ServiceContext ctx, DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) throws Exception {
591 boolean updateRevNumber = this.getShouldUpdateRevNumber();
592 Boolean contextProperty = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
593 if (contextProperty != null) {
594 updateRevNumber = contextProperty;
597 if (updateRevNumber == true) { // We don't update the rev number of synchronization requests
598 updateRevNumbers(wrapDoc);
603 public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
604 super.handleCreate(wrapDoc);
606 // Uncomment once debugged and App layer is read to integrate
607 // Experimenting with this uncommented now ...
608 handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityCommonSchemaName);
609 updateRefnameForAuthority(wrapDoc, authorityCommonSchemaName);//CSPACE-3178
612 protected String buildWhereForShortId(String name) {
613 return authorityCommonSchemaName
614 + ":" + AuthorityJAXBSchema.SHORT_IDENTIFIER
618 private boolean isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
622 private boolean temp_isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
623 boolean result = true;
625 ServiceContext ctx = this.getServiceContext();
626 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
627 String nxqlWhereClause = buildWhereForShortId(shortIdentifier);
629 DocumentWrapper<DocumentModel> searchResultWrapper = getRepositoryClient(ctx).findDoc(ctx, nxqlWhereClause);
630 if (searchResultWrapper != null) {
632 if (logger.isInfoEnabled() == true) {
633 DocumentModel searchResult = searchResultWrapper.getWrappedObject();
634 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'",
635 shortIdentifier, searchResult.getName());
636 logger.trace(debugMsg);
639 } catch (DocumentNotFoundException e) {
640 // Not a problem, just means we couldn't find another authority with that short ID
647 * If no short identifier was provided in the input payload,
648 * generate a short identifier from the display name. Either way though,
649 * the short identifier needs to be unique.
651 private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
652 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
653 String displayName = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.DISPLAY_NAME);
654 String shortDisplayName = "";
655 String generateShortIdentifier = null;
656 if (Tools.isEmpty(shortIdentifier)) {
657 generateShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(displayName, shortDisplayName);
658 docModel.setProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER, shortIdentifier);
661 if (isUnique(docModel, schemaName) == false) {
662 String shortId = generateShortIdentifier == null ? shortIdentifier : generateShortIdentifier;
663 String errMsgVerb = generateShortIdentifier == null ? "supplied" : "generated";
664 String errMsg = String.format("The %s short identifier '%s' was not unique, so the new authority could not be created.",
665 errMsgVerb, shortId);
666 throw new DocumentException(errMsg);
671 * Generate a refName for the authority from the short identifier
674 * All refNames for authorities are generated. If a client supplies
675 * a refName, it will be overwritten during create (per this method)
676 * or discarded during update (per filterReadOnlyPropertiesForPart).
678 * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
681 protected void updateRefnameForAuthority(DocumentWrapper<DocumentModel> wrapDoc, String schemaName) throws Exception {
682 DocumentModel docModel = wrapDoc.getWrappedObject();
683 RefName.Authority authority = (Authority) getRefName(getServiceContext(), docModel);
684 String refName = authority.toString();
685 docModel.setProperty(schemaName, AuthorityJAXBSchema.REF_NAME, refName);
689 public RefName.RefNameInterface getRefName(ServiceContext ctx,
690 DocumentModel docModel) {
691 RefName.RefNameInterface refname = null;
694 String shortIdentifier = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
695 String displayName = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.DISPLAY_NAME);
696 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
697 ctx.getServiceName(),
698 null, // Only use shortId form!!!
702 } catch (Exception e) {
703 logger.error(e.getMessage(), e);
710 protected String getRefnameDisplayName(DocumentWrapper<DocumentModel> docWrapper) {
711 String result = null;
713 DocumentModel docModel = docWrapper.getWrappedObject();
714 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
715 RefName.Authority refname = (RefName.Authority)getRefName(ctx, docModel);
716 result = refname.getDisplayName();
721 public String getShortIdentifier(ServiceContext ctx, String authCSID, String schemaName) throws Exception {
722 String shortIdentifier = null;
723 CoreSessionInterface repoSession = null;
724 boolean releaseSession = false;
726 NuxeoRepositoryClientImpl nuxeoRepoClient = (NuxeoRepositoryClientImpl)this.getRepositoryClient(ctx);
728 repoSession = nuxeoRepoClient.getRepositorySession(ctx);
729 DocumentWrapper<DocumentModel> wrapDoc = nuxeoRepoClient.getDocFromCsid(ctx, repoSession, authCSID);
730 DocumentModel docModel = wrapDoc.getWrappedObject();
731 if (docModel == null) {
732 throw new DocumentNotFoundException(String.format("Could not find authority resource with CSID='%s'.", authCSID));
734 shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
735 } catch (ClientException ce) {
736 throw new RuntimeException("AuthorityDocHandler Internal Error: cannot get shortId!", ce);
738 if (repoSession != null) {
739 nuxeoRepoClient.releaseRepositorySession(ctx, repoSession);
743 return shortIdentifier;
747 * Filters out selected values supplied in an update request.
749 * @param objectProps the properties filtered out from the update payload
750 * @param partMeta metadata for the object to fill
753 public void filterReadOnlyPropertiesForPart(
754 Map<String, Object> objectProps, ObjectPartType partMeta) {
755 super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
756 String commonPartLabel = getServiceContext().getCommonPartLabel();
757 if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
758 objectProps.remove(AuthorityJAXBSchema.CSID);
759 objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
760 objectProps.remove(AuthorityJAXBSchema.REF_NAME);
765 protected Object getListResultValue(DocumentModel docModel, // REM - CSPACE-5133
766 String schema, ListResultField field) throws DocumentException {
767 Object result = null;
769 result = super.getListResultValue(docModel, schema, field);