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;
66 import org.dom4j.Element;
67 import org.eclipse.jetty.http.HttpStatus;
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.
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
497 private void assertStatusCode(Response res, Specifier specifier, AuthorityClient client) throws Exception {
498 int statusCode = res.getStatus();
500 if (statusCode != HttpStatus.OK_200) {
501 String errMsg = String.format("Could not retrieve authority information for '%s' on remote server '%s'. Server returned status code %d",
502 specifier.getURNValue(), client.getBaseURL(), statusCode);
503 throw new DocumentException(statusCode, errMsg);
508 * Request an authority item list payload from the SAS server. This is a non-paging solution. If the authority
509 * has a very large number of items/terms, we might not be able to handle them all.
516 private PoxPayloadIn requestPayloadInItemList(ServiceContext ctx, Specifier specifier) throws Exception {
517 PoxPayloadIn result = null;
518 AuthorityClient client = (AuthorityClient) ctx.getClient();
521 // First find out how many items exist
522 Response res = client.readItemList(specifier.getURNValue(),
523 null, // partial term string
524 null, // keyword string
528 assertStatusCode(res, specifier, client);
529 AbstractCommonList commonList;
531 commonList = res.readEntity(AbstractCommonList.class);
535 long numOfItems = commonList.getTotalItems();
538 // Next, request a payload list with all the items
539 res = client.readItemList(specifier.getURNValue(),
540 null, // partial term string
541 null, // keyword string
542 numOfItems, // page size
545 assertStatusCode(res, specifier, client);
547 result = new PoxPayloadIn((String)res.readEntity(getEntityResponseType())); // Get the entire response.
557 * Non standard injection of CSID into common part, since caller may access through
558 * shortId, and not know the CSID.
559 * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
562 protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
564 Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
566 // Add the CSID to the common part
567 if (partMeta.getLabel().equalsIgnoreCase(authorityCommonSchemaName)) {
568 String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
569 unQObjectProperties.put("csid", csid);
572 return unQObjectProperties;
575 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
576 super.fillAllParts(wrapDoc, action);
578 // Update the record's revision number on both CREATE and UPDATE actions, but not on SYNC
580 if (this.getShouldUpdateRevNumber() == true) { // We won't update rev numbers on synchronization with SAS
581 updateRevNumbers(wrapDoc);
585 protected void updateRevNumbers(DocumentWrapper<DocumentModel> wrapDoc) {
586 DocumentModel documentModel = wrapDoc.getWrappedObject();
587 Long rev = (Long)documentModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV);
593 documentModel.setProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV, rev);
597 * We consider workflow state changes as changes that should bump the revision number
599 * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#handleWorkflowTransition(org.collectionspace.services.common.document.DocumentWrapper, org.collectionspace.services.lifecycle.TransitionDef)
602 public void handleWorkflowTransition(ServiceContext ctx, DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) throws Exception {
603 boolean updateRevNumber = this.getShouldUpdateRevNumber();
604 Boolean contextProperty = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
605 if (contextProperty != null) {
606 updateRevNumber = contextProperty;
609 if (updateRevNumber == true) { // We don't update the rev number of synchronization requests
610 updateRevNumbers(wrapDoc);
615 public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
616 super.handleCreate(wrapDoc);
618 // Uncomment once debugged and App layer is read to integrate
619 // Experimenting with this uncommented now ...
620 handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityCommonSchemaName);
621 updateRefnameForAuthority(wrapDoc, authorityCommonSchemaName);//CSPACE-3178
624 protected String buildWhereForShortId(String name) {
625 return authorityCommonSchemaName
626 + ":" + AuthorityJAXBSchema.SHORT_IDENTIFIER
630 private boolean isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
634 private boolean temp_isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
635 boolean result = true;
637 ServiceContext ctx = this.getServiceContext();
638 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
639 String nxqlWhereClause = buildWhereForShortId(shortIdentifier);
641 DocumentWrapper<DocumentModel> searchResultWrapper = getRepositoryClient(ctx).findDoc(ctx, nxqlWhereClause);
642 if (searchResultWrapper != null) {
644 if (logger.isInfoEnabled() == true) {
645 DocumentModel searchResult = searchResultWrapper.getWrappedObject();
646 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'",
647 shortIdentifier, searchResult.getName());
648 logger.trace(debugMsg);
651 } catch (DocumentNotFoundException e) {
652 // Not a problem, just means we couldn't find another authority with that short ID
659 * If no short identifier was provided in the input payload,
660 * generate a short identifier from the display name. Either way though,
661 * the short identifier needs to be unique.
663 private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
664 String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
665 String displayName = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.DISPLAY_NAME);
666 String shortDisplayName = "";
667 String generateShortIdentifier = null;
668 if (Tools.isEmpty(shortIdentifier)) {
669 generateShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(displayName, shortDisplayName);
670 docModel.setProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER, shortIdentifier);
673 if (isUnique(docModel, schemaName) == false) {
674 String shortId = generateShortIdentifier == null ? shortIdentifier : generateShortIdentifier;
675 String errMsgVerb = generateShortIdentifier == null ? "supplied" : "generated";
676 String errMsg = String.format("The %s short identifier '%s' was not unique, so the new authority could not be created.",
677 errMsgVerb, shortId);
678 throw new DocumentException(errMsg);
683 * Generate a refName for the authority from the short identifier
686 * All refNames for authorities are generated. If a client supplies
687 * a refName, it will be overwritten during create (per this method)
688 * or discarded during update (per filterReadOnlyPropertiesForPart).
690 * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
693 protected void updateRefnameForAuthority(DocumentWrapper<DocumentModel> wrapDoc, String schemaName) throws Exception {
694 DocumentModel docModel = wrapDoc.getWrappedObject();
695 RefName.Authority authority = (Authority) getRefName(getServiceContext(), docModel);
696 String refName = authority.toString();
697 docModel.setProperty(schemaName, AuthorityJAXBSchema.REF_NAME, refName);
701 public RefName.RefNameInterface getRefName(ServiceContext ctx,
702 DocumentModel docModel) {
703 RefName.RefNameInterface refname = null;
706 String shortIdentifier = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
707 String displayName = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.DISPLAY_NAME);
708 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
709 ctx.getServiceName(),
710 null, // Only use shortId form!!!
714 } catch (Exception e) {
715 logger.error(e.getMessage(), e);
722 protected String getRefnameDisplayName(DocumentWrapper<DocumentModel> docWrapper) {
723 String result = null;
725 DocumentModel docModel = docWrapper.getWrappedObject();
726 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
727 RefName.Authority refname = (RefName.Authority)getRefName(ctx, docModel);
728 result = refname.getDisplayName();
733 public String getShortIdentifier(ServiceContext ctx, String authCSID, String schemaName) throws Exception {
734 String shortIdentifier = null;
735 CoreSessionInterface repoSession = null;
736 boolean releaseSession = false;
738 RepositoryClientImpl nuxeoRepoClient = (RepositoryClientImpl)this.getRepositoryClient(ctx);
740 repoSession = nuxeoRepoClient.getRepositorySession(ctx);
741 DocumentWrapper<DocumentModel> wrapDoc = nuxeoRepoClient.getDocFromCsid(ctx, repoSession, authCSID);
742 DocumentModel docModel = wrapDoc.getWrappedObject();
743 if (docModel == null) {
744 throw new DocumentNotFoundException(String.format("Could not find authority resource with CSID='%s'.", authCSID));
746 shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
747 } catch (ClientException ce) {
748 throw new RuntimeException("AuthorityDocHandler Internal Error: cannot get shortId!", ce);
750 if (repoSession != null) {
751 nuxeoRepoClient.releaseRepositorySession(ctx, repoSession);
755 return shortIdentifier;
759 * Filters out selected values supplied in an update request.
761 * @param objectProps the properties filtered out from the update payload
762 * @param partMeta metadata for the object to fill
765 public void filterReadOnlyPropertiesForPart(
766 Map<String, Object> objectProps, ObjectPartType partMeta) {
767 super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
768 String commonPartLabel = getServiceContext().getCommonPartLabel();
769 if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
770 objectProps.remove(AuthorityJAXBSchema.CSID);
771 objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
772 objectProps.remove(AuthorityJAXBSchema.REF_NAME);