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.ObjectPartType;
60 import org.collectionspace.services.jaxb.AbstractCommonList;
61 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
62 import org.collectionspace.services.lifecycle.TransitionDef;
63 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler;
64 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
65 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
66 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 || 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
149 // First, sync all the authority items
151 syncAllItems(ctx, sasSpecifier); // FIXME: We probably want to consider "paging" this instead of handling the entire set of items.
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.
262 private long deleteOrDeprecateItems(ServiceContext ctx, Specifier authoritySpecifier, ArrayList<String> itemShortIdList) throws Exception {
264 AuthorityItemSpecifier authorityItemSpecificer = null;
266 ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, false); // Don't update the revision number when we delete or deprecate the item
267 for (String itemShortId:itemShortIdList) {
268 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
270 authorityItemSpecificer = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authoritySpecifier.value,
272 authorityResource.deleteAuthorityItem(ctx,
273 authorityItemSpecificer.getParentSpecifier().getURNValue(),
274 authorityItemSpecificer.getItemSpecifier().getURNValue(),
275 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)
277 } catch (DocumentReferenceException de) {
278 logger.info(String.format("Authority item with '%s' has existing references and cannot be removed during sync.",
279 authorityItemSpecificer), de);
280 boolean marked = AuthorityServiceUtils.markAuthorityItemAsDeprecated(ctx, authorityItemCommonSchemaName,
281 authorityItemSpecificer);
282 if (marked == true) {
285 } catch (Exception e) {
286 logger.warn(String.format("Unable to delete authority item '%s'", authorityItemSpecificer), e);
291 if (logger.isWarnEnabled() == true) {
292 if (result != itemShortIdList.size()) {
293 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.",
294 result, itemShortIdList.size()));
302 * Gets the list of SAS related items in the local authority. We exlude items with the "proposed" flags because
303 * we want a list with only SAS created items.
305 * We need to add pagination support to this call!!!
308 * @param authoritySpecifier
312 private ArrayList<String> getItemsInLocalAuthority(ServiceContext ctx, Specifier authoritySpecifier) throws Exception {
313 ArrayList<String> result = new ArrayList<String>();
315 ResourceMap resourceMap = ctx.getResourceMap();
316 String resourceName = ctx.getClient().getServiceName();
317 AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
318 AbstractCommonList acl = authorityResource.getAuthorityItemList(ctx, authoritySpecifier.getURNValue(), ctx.getUriInfo());
320 List<ListItem> listItemList = acl.getListItem();
321 for (ListItem listItem:listItemList) {
322 Boolean proposed = getBooleanValue(listItem, AuthorityItemJAXBSchema.PROPOSED);
323 if (proposed == false) { // exclude "proposed" (i.e., local-only items)
324 result.add(AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.SHORT_IDENTIFIER));
331 private Boolean getBooleanValue(ListItem listItem, String name) {
332 Boolean result = null;
334 String value = AbstractCommonListUtils.ListItemGetElementValue(listItem, name);
336 result = Boolean.valueOf(value);
342 private String getStringValue(ListItem listItem, String name) {
343 return AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME);
347 * This method should only be used during a SAS synchronization request.
350 * @param parentIdentifier - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
351 * @param itemIdentifier - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
354 protected void createLocalItem(ServiceContext ctx, String parentIdentifier, String itemIdentifier, Boolean syncHierarchicalRelationships) throws Exception {
356 // Create a URN short ID specifier for the getting a copy of the remote authority item
358 Specifier authoritySpecifier = Specifier.getSpecifier(parentIdentifier);
359 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier);
360 AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(authoritySpecifier, itemSpecifier);
362 // Get the remote client configuration name
364 DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, authoritySpecifier);
365 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
367 // Get the remote payload
369 PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadInFromRemoteServer(sasAuthorityItemSpecifier, remoteClientConfigName,
370 ctx.getServiceName(), getEntityResponseType(), syncHierarchicalRelationships);
371 sasPayloadIn = AuthorityServiceUtils.filterRefnameDomains(ctx, sasPayloadIn); // We need to filter domain name part of any and all refnames in the payload
373 // Using the payload from the remote server, create a local copy of the item
375 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
376 Response response = authorityResource.createAuthorityItemWithParentContext(ctx, authoritySpecifier.getURNValue(),
377 sasPayloadIn, AuthorityServiceUtils.DONT_UPDATE_REV, AuthorityServiceUtils.NOT_PROPOSED, AuthorityServiceUtils.SAS_ITEM);
379 // Check the response for successful POST result
381 if (response.getStatus() != Response.Status.CREATED.getStatusCode()) {
382 throw new DocumentException(String.format("Could not create new authority item '%s' during synchronization of the '%s' authority.",
383 itemIdentifier, parentIdentifier));
386 // Since we're creating an item that was sourced from the replication server, we need to replicate it locally.
388 authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier,
389 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)
393 * Try to synchronize a remote item (using its refName) with a local item. If the local doesn't yet
394 * exist, we'll create it.
396 * -1 = sync not needed; i.e., already in sync
398 * 1 = local item was missing so we created it
404 protected long syncRemoteItemWithLocalItem(ServiceContext ctx, String itemRefName) throws Exception {
407 // Using the item refname (with no local CSID), create specifiers that we'll use to find the local versions
409 AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(itemRefName);
410 String parentIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name);
411 String itemIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.name);
413 // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
415 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
416 PoxPayloadOut localItemPayloadOut;
418 localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
419 } catch (DocumentNotFoundException dnf) {
421 // Document not found, means we need to create an item/term that exists only on the SAS
423 logger.info(String.format("Remote item with refname='%s' doesn't exist locally, so we'll create it.", itemRefName));
424 createLocalItem(ctx, parentIdentifier, itemIdentifier, AuthorityClient.DONT_INCLUDE_RELATIONS);
425 return 1; // exit with status of 1 means we created a new authority item
428 // If we get here, we know the item exists both locally and remotely, so we need to synchronize them.
432 PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, false);
433 if (theUpdate != null) {
434 result = 0; // means we needed to sync this item with SAS
435 logger.debug(String.format("Sync'd authority item parent='%s' id='%s with SAS. Updated payload is: \n%s",
436 parentIdentifier, itemIdentifier, theUpdate.getXmlPayload()));
438 } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
440 logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
444 return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
448 * Ensure the local items relationships look the same as the remote items' by synchronizing the hierarchy relationship records
449 * of the SAS item with the local item.
456 protected long syncRemoteItemRelationshipsWithLocalItem(ServiceContext ctx, Specifier authoritySpecifier, String itemShortId) throws Exception {
459 String parentIdentifier = authoritySpecifier.getURNValue();
460 String itemIdentifier = Specifier.createShortIdURNValue(itemShortId);
462 // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
464 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
465 PoxPayloadOut localItemPayloadOut;
467 MultivaluedMap queryParams = ctx.getQueryParams();
468 localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
469 } catch (DocumentNotFoundException dnf) {
471 // Document not found, means we need to create an item/term that exists only on the SAS
473 logger.info(String.format("Remote item with short ID ='%s' doesn't exist locally, so we can't synchronize its relationships.", itemShortId));
477 // If we get here, we know the item exists both locally and remotely, so we need to synchronize the hierarchy relationships.
481 PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, AuthorityClient.INCLUDE_RELATIONS);
482 if (theUpdate != null) {
483 result = 0; // means we needed to sync this item with SAS
484 logger.debug(String.format("Sync'd authority item parent='%s' id='%s with SAS. Updated payload is: \n%s",
485 parentIdentifier, itemIdentifier, theUpdate.getXmlPayload()));
487 } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
489 logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
493 return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
498 private void assertStatusCode(Response res, Specifier specifier, AuthorityClient client) throws Exception {
499 int statusCode = res.getStatus();
501 if (statusCode != HttpStatus.SC_OK) {
502 String errMsg = String.format("Could not retrieve authority information for '%s' on remote server '%s'. Server returned status code %d",
503 specifier.getURNValue(), client.getBaseURL(), statusCode);
504 throw new DocumentException(statusCode, errMsg);
509 * Request an authority item list payload from the SAS server. This is a non-paging solution. If the authority
510 * has a very large number of items/terms, we might not be able to handle them all.
517 private PoxPayloadIn requestPayloadInItemList(ServiceContext ctx, Specifier specifier) throws Exception {
518 PoxPayloadIn result = null;
519 AuthorityClient client = (AuthorityClient) ctx.getClient();
522 // First find out how many items exist
523 Response res = client.readItemList(specifier.getURNValue(),
524 null, // partial term string
525 null, // keyword string
529 assertStatusCode(res, specifier, client);
530 AbstractCommonList commonList;
532 commonList = res.readEntity(AbstractCommonList.class);
536 long numOfItems = commonList.getTotalItems();
539 // Next, request a payload list with all the items
540 res = client.readItemList(specifier.getURNValue(),
541 null, // partial term string
542 null, // keyword string
543 numOfItems, // page size
546 assertStatusCode(res, specifier, client);
548 result = new PoxPayloadIn((String)res.readEntity(getEntityResponseType())); // Get the entire response.
558 * Non standard injection of CSID into common part, since caller may access through
559 * shortId, and not know the CSID.
560 * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
563 protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
565 Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
567 // Add the CSID to the common part
568 if (partMeta.getLabel().equalsIgnoreCase(authorityCommonSchemaName)) {
569 String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
570 unQObjectProperties.put("csid", csid);
573 return unQObjectProperties;
576 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
577 super.fillAllParts(wrapDoc, action);
579 // Update the record's revision number on both CREATE and UPDATE actions, but not on SYNC
581 if (this.getShouldUpdateRevNumber() == true) { // We won't update rev numbers on synchronization with SAS
582 updateRevNumbers(wrapDoc);
586 protected void updateRevNumbers(DocumentWrapper<DocumentModel> wrapDoc) {
587 DocumentModel documentModel = wrapDoc.getWrappedObject();
588 Long rev = (Long)documentModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV);
594 documentModel.setProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV, rev);
598 * We consider workflow state changes as changes that should bump the revision number
600 * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#handleWorkflowTransition(org.collectionspace.services.common.document.DocumentWrapper, org.collectionspace.services.lifecycle.TransitionDef)
603 public void handleWorkflowTransition(ServiceContext ctx, DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) throws Exception {
604 boolean updateRevNumber = this.getShouldUpdateRevNumber();
605 Boolean contextProperty = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
606 if (contextProperty != null) {
607 updateRevNumber = contextProperty;
610 if (updateRevNumber == true) { // We don't update the rev number of synchronization requests
611 updateRevNumbers(wrapDoc);
616 public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
617 super.handleCreate(wrapDoc);
619 // Uncomment once debugged and App layer is read to integrate
620 // Experimenting with this uncommented now ...
621 handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityCommonSchemaName);
622 updateRefnameForAuthority(wrapDoc, authorityCommonSchemaName);//CSPACE-3178
625 protected String buildWhereForShortId(String name) {
626 return authorityCommonSchemaName
627 + ":" + AuthorityJAXBSchema.SHORT_IDENTIFIER
631 private boolean isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
635 private boolean temp_isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
636 boolean result = true;
638 ServiceContext ctx = this.getServiceContext();
639 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
640 String nxqlWhereClause = buildWhereForShortId(shortIdentifier);
642 DocumentWrapper<DocumentModel> searchResultWrapper = getRepositoryClient(ctx).findDoc(ctx, nxqlWhereClause);
643 if (searchResultWrapper != null) {
645 if (logger.isInfoEnabled() == true) {
646 DocumentModel searchResult = searchResultWrapper.getWrappedObject();
647 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'",
648 shortIdentifier, searchResult.getName());
649 logger.trace(debugMsg);
652 } catch (DocumentNotFoundException e) {
653 // Not a problem, just means we couldn't find another authority with that short ID
660 * If no short identifier was provided in the input payload,
661 * generate a short identifier from the display name. Either way though,
662 * the short identifier needs to be unique.
664 private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
665 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
666 String displayName = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.DISPLAY_NAME);
667 String shortDisplayName = "";
668 String generateShortIdentifier = null;
669 if (Tools.isEmpty(shortIdentifier)) {
670 generateShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(displayName, shortDisplayName);
671 docModel.setProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER, shortIdentifier);
674 if (isUnique(docModel, schemaName) == false) {
675 String shortId = generateShortIdentifier == null ? shortIdentifier : generateShortIdentifier;
676 String errMsgVerb = generateShortIdentifier == null ? "supplied" : "generated";
677 String errMsg = String.format("The %s short identifier '%s' was not unique, so the new authority could not be created.",
678 errMsgVerb, shortId);
679 throw new DocumentException(errMsg);
684 * Generate a refName for the authority from the short identifier
687 * All refNames for authorities are generated. If a client supplies
688 * a refName, it will be overwritten during create (per this method)
689 * or discarded during update (per filterReadOnlyPropertiesForPart).
691 * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
694 protected void updateRefnameForAuthority(DocumentWrapper<DocumentModel> wrapDoc, String schemaName) throws Exception {
695 DocumentModel docModel = wrapDoc.getWrappedObject();
696 RefName.Authority authority = (Authority) getRefName(getServiceContext(), docModel);
697 String refName = authority.toString();
698 docModel.setProperty(schemaName, AuthorityJAXBSchema.REF_NAME, refName);
702 public RefName.RefNameInterface getRefName(ServiceContext ctx,
703 DocumentModel docModel) {
704 RefName.RefNameInterface refname = null;
707 String shortIdentifier = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
708 String displayName = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.DISPLAY_NAME);
709 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
710 ctx.getServiceName(),
711 null, // Only use shortId form!!!
715 } catch (Exception e) {
716 logger.error(e.getMessage(), e);
723 protected String getRefnameDisplayName(DocumentWrapper<DocumentModel> docWrapper) {
724 String result = null;
726 DocumentModel docModel = docWrapper.getWrappedObject();
727 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
728 RefName.Authority refname = (RefName.Authority)getRefName(ctx, docModel);
729 result = refname.getDisplayName();
734 public String getShortIdentifier(ServiceContext ctx, String authCSID, String schemaName) throws Exception {
735 String shortIdentifier = null;
736 CoreSessionInterface repoSession = null;
737 boolean releaseSession = false;
739 NuxeoRepositoryClientImpl nuxeoRepoClient = (NuxeoRepositoryClientImpl)this.getRepositoryClient(ctx);
741 repoSession = nuxeoRepoClient.getRepositorySession(ctx);
742 DocumentWrapper<DocumentModel> wrapDoc = nuxeoRepoClient.getDocFromCsid(ctx, repoSession, authCSID);
743 DocumentModel docModel = wrapDoc.getWrappedObject();
744 if (docModel == null) {
745 throw new DocumentNotFoundException(String.format("Could not find authority resource with CSID='%s'.", authCSID));
747 shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
748 } catch (ClientException ce) {
749 throw new RuntimeException("AuthorityDocHandler Internal Error: cannot get shortId!", ce);
751 if (repoSession != null) {
752 nuxeoRepoClient.releaseRepositorySession(ctx, repoSession);
756 return shortIdentifier;
760 * Filters out selected values supplied in an update request.
762 * @param objectProps the properties filtered out from the update payload
763 * @param partMeta metadata for the object to fill
766 public void filterReadOnlyPropertiesForPart(
767 Map<String, Object> objectProps, ObjectPartType partMeta) {
768 super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
769 String commonPartLabel = getServiceContext().getCommonPartLabel();
770 if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
771 objectProps.remove(AuthorityJAXBSchema.CSID);
772 objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
773 objectProps.remove(AuthorityJAXBSchema.REF_NAME);