]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
a6a12ff359f368a9eec7cfb1a1b7a955914a83aa
[tmp/jakarta-migration.git] /
1 /**
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:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
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.
23  */
24 package org.collectionspace.services.common.vocabulary.nuxeo;
25
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Map;
29
30 import javax.ws.rs.core.Response;
31
32 import org.collectionspace.services.client.AbstractCommonListUtils;
33 import org.collectionspace.services.client.AuthorityClient;
34 import org.collectionspace.services.client.CollectionSpaceClient;
35 import org.collectionspace.services.client.PayloadInputPart;
36 import org.collectionspace.services.client.VocabularyClient;
37 import org.collectionspace.services.client.PoxPayloadIn;
38 import org.collectionspace.services.client.PoxPayloadOut;
39 import org.collectionspace.services.client.workflow.WorkflowClient;
40 import org.collectionspace.services.common.ResourceMap;
41 import org.collectionspace.services.common.XmlTools;
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.AuthorityInfo;
46 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
47 import org.collectionspace.services.common.api.Tools;
48 import org.collectionspace.services.common.context.ServiceContext;
49 import org.collectionspace.services.common.document.DocumentException;
50 import org.collectionspace.services.common.document.DocumentHandler;
51 import org.collectionspace.services.common.document.DocumentNotFoundException;
52 import org.collectionspace.services.common.document.DocumentWrapper;
53 import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
54 import org.collectionspace.services.common.vocabulary.AuthorityJAXBSchema;
55 import org.collectionspace.services.common.vocabulary.AuthorityResource;
56 import org.collectionspace.services.common.vocabulary.AuthorityServiceUtils;
57 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
58 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
59 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
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.RepositoryClientImpl;
67 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
68 import org.dom4j.Document;
69 import org.dom4j.Element;
70 import org.nuxeo.ecm.core.api.ClientException;
71 import org.nuxeo.ecm.core.api.DocumentModel;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74
75 /**
76  * AuthorityDocumentModelHandler
77  *
78  * $LastChangedRevision: $
79  * $LastChangedDate: $
80  */
81 public abstract class AuthorityDocumentModelHandler<AuthCommon>
82         extends NuxeoDocumentModelHandler<AuthCommon> {
83     
84         private final Logger logger = LoggerFactory.getLogger(AuthorityDocumentModelHandler.class);     
85     
86         protected String authorityCommonSchemaName;
87     protected String authorityItemCommonSchemaName;
88     protected boolean shouldUpdateRevNumber = true; // default to updating the revision number
89
90     public AuthorityDocumentModelHandler(String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
91         this.authorityCommonSchemaName = authorityCommonSchemaName;
92         this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
93     }
94     
95     public void setShouldUpdateRevNumber(boolean flag) {
96         this.shouldUpdateRevNumber = flag;
97     }
98     
99     public boolean getShouldUpdateRevNumber() {
100         return this.shouldUpdateRevNumber;
101     }
102     
103     /**
104      * The entity type expected from the JAX-RS Response object
105      */
106     public Class<String> getEntityResponseType() {
107         return String.class;
108     }
109     
110     @Override
111     public void prepareSync() throws Exception {
112         this.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV);  // Never update rev nums on sync operations
113     }
114
115     protected PayloadInputPart extractPart(Response res, String partLabel)
116             throws Exception {
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.");
121             }
122             return payloadInputPart;
123     }
124     
125     @Override
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();
130         //
131         // Get the rev number of the authority so we can compare with rev number of shared authority
132         //
133         DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, specifier);
134         Long localRev = (Long) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REV);
135         String shortId = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
136         //
137         // Using the short ID of the local authority, create a URN specifier to retrieve the SAS authority
138         //
139         Specifier sasSpecifier = new Specifier(SpecifierForm.URN_NAME, shortId);
140         PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadIn(ctx, sasSpecifier, getEntityResponseType());
141         //
142         // If the authority on the SAS is newer, synch all the items and then the authority record as well
143         //
144         //
145         Long sasRev = getRevision(sasPayloadIn);
146         if (sasRev > localRev) {
147                 //
148                 // First, sync all the authority items
149                 //
150                 syncAllItems(ctx, sasSpecifier);
151                 //
152                 // Next, sync the authority resource/record itself
153                 //
154                 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
155                 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
156                 PoxPayloadOut payloadOut = authorityResource.update(ctx, ctx.getResourceMap(), ctx.getUriInfo(), docModel.getName(), 
157                                 sasPayloadIn);
158                 if (payloadOut != null) {
159                         ctx.setOutput(payloadOut);
160                         result = true;
161                 }
162         }
163         
164         return result;
165     }
166     
167     /*
168      * Get the list of authority items from the remote shared authority server (SAS) and try
169      * to synchronize them with the local items.  If items exist on the remote but not the local, we'll create them.
170      */
171     protected int syncAllItems(ServiceContext ctx, Specifier sasAuthoritySpecifier) throws Exception {
172         int result = -1;
173         int created = 0;
174         int synched = 0;
175         int alreadySynched = 0;
176         int deprecated = 0;
177         int totalItemsProcessed = 0;
178         ArrayList<String> itemsInRemoteAuthority = new ArrayList<String>();
179         //
180         // Iterate over the list of items/terms in the remote authority
181         //
182         PoxPayloadIn sasPayloadInItemList = requestPayloadInItemList(ctx, sasAuthoritySpecifier);
183         List<Element> itemList = getItemList(sasPayloadInItemList);
184         if (itemList != null) {
185                 for (Element e:itemList) {
186                         String remoteRefName = XmlTools.getElementValue(e, "refName");
187                         itemsInRemoteAuthority.add(remoteRefName);
188                         long status = syncRemoteItemWithLocalItem(ctx, remoteRefName);
189                         if (status == 1) {
190                                 created++;
191                         } else if (status == 0) {
192                                 synched++;
193                         } else {
194                                 alreadySynched++;
195                         }
196                         totalItemsProcessed++;
197                 }
198         }
199         // Now see if we need to deprecate or delete items that have been hard-deleted from the SAS but still exist
200         // locally.  Subtract (remove) the list of remote items from the list of local items to determine which
201         // of the remote items have been hard deleted.
202         //
203         ArrayList<String> itemsInLocalAuthority = getItemsInLocalAuthority(ctx, sasAuthoritySpecifier);
204         if (itemsInLocalAuthority.removeAll(itemsInRemoteAuthority) == true) {
205                 ArrayList<String> remainingItems = itemsInLocalAuthority; // now a subset of local items
206                 //
207                 // We now need to either hard-deleted or deprecate the remaining authorities
208                 //
209                 deleteOrDeprecateItems(ctx, remainingItems);
210         }
211
212         
213         logger.info(String.format("Total number of items processed during sync: %d", totalItemsProcessed));
214         logger.info(String.format("Number of items synchronized: %d", synched));
215         logger.info(String.format("Number of items created during sync: %d", created));
216         logger.info(String.format("Number not needing synchronization: %d", alreadySynched));
217
218         return result;
219     }
220
221     /**
222      * 
223      * @param ctx
224      * @param refNameList
225      * @return
226      * @throws Exception
227      */
228     private long deleteOrDeprecateItems(ServiceContext ctx, ArrayList<String> refNameList) throws Exception {
229         long result = 0;
230         
231         ArrayList<String> failureList = new ArrayList<String>();
232         for (String refName:refNameList) {
233                 AuthorityTermInfo itemInfo = RefNameUtils.parseAuthorityTermInfo(refName);
234                 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
235                 try {
236                         authorityResource.deleteAuthorityItem(ctx, itemInfo.inAuthority.csid, itemInfo.csid);
237                         result++;
238                 } catch (DocumentException de) {
239                         logger.info(String.format("Hit document exception trying to delete or deprecate '%s' during sync",
240                                         refName), de);
241                         boolean marked = markAuthorityItemAsDeprecated(ctx, itemInfo);
242                         if (marked == true) {
243                                 result++;
244                         }
245                 } catch (Exception e) {
246                         logger.warn(String.format("Unable to delete authority item '%s'", refName), e);
247                 }
248         }
249
250         if (logger.isWarnEnabled() == true) {
251                 if (result != refNameList.size()) {
252                         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.",
253                                         result, refNameList.size()));
254                 }
255         }
256         
257         return result;
258     }
259     
260     /**
261      * Mark the authority item as deprecated.
262      * 
263      * @param ctx
264      * @param itemInfo
265      * @throws Exception
266      */
267     private boolean markAuthorityItemAsDeprecated(ServiceContext ctx, AuthorityTermInfo itemInfo) throws Exception {
268         boolean result = false;
269         
270         try {
271                 String itemCsid = itemInfo.csid;
272                 DocumentModel docModel = NuxeoUtils.getDocFromCsid(ctx, (CoreSessionInterface)ctx.getCurrentRepositorySession(), itemCsid);
273                 docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.DEPRECATED,
274                                 new Boolean(AuthorityServiceUtils.DEPRECATED));
275                 CoreSessionInterface session = (CoreSessionInterface) ctx.getCurrentRepositorySession();
276                 session.saveDocument(docModel);
277                 result = true;
278         } catch (Exception e) {
279                 logger.warn(String.format("Could not mark item '%s' as deprecated.", itemInfo.name), e);
280         }
281         
282         return result;
283     }
284     
285     /**
286      * Gets the list of SAS related items in the local authority.  We exlude items with the "proposed" flags because
287      * we want a list with only SAS created items.
288      * 
289      * We need to add pagination support to this call!!!
290      * 
291      * @param ctx
292      * @param specifier
293      * @return
294      * @throws Exception
295      */
296     private ArrayList<String> getItemsInLocalAuthority(ServiceContext ctx, Specifier specifier) throws Exception {
297         ArrayList<String> result = new ArrayList<String>();
298         
299         ResourceMap resourceMap = ctx.getResourceMap();
300         String resourceName = ctx.getClient().getServiceName();
301         AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
302         AbstractCommonList acl = authorityResource.getAuthorityItemList(ctx, specifier.value, ctx.getUriInfo());
303         List<ListItem> listItemList = acl.getListItem();
304         for (ListItem listItem:listItemList) {
305                 Boolean proposed = getBooleanValue(listItem, AuthorityItemJAXBSchema.PROPOSED);
306                 if (proposed == false) { // exclude "proposed" (i.e., local-only items)
307                         result.add(AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME));
308                 }
309         }
310         
311         return result;
312     }
313     
314     private Boolean getBooleanValue(ListItem listItem, String name) {
315         Boolean result = null;
316         
317                 String value = AbstractCommonListUtils.ListItemGetElementValue(listItem, name);
318                 if (value != null) {
319                         result = Boolean.valueOf(value);
320                 }
321                 
322                 return result;
323     }
324     
325     private String getStringValue(ListItem listItem, String name) {
326         return AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME);
327     }
328     
329     /**
330      * This is a sync method.
331      * @param ctx
332      * @param parentIdentifier - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
333      * @param itemIdentifier   - Must be in short-id-refname form -i.e., urn:cspace:name(shortid)
334      * @throws Exception 
335      */
336     protected void createLocalItem(ServiceContext ctx, String parentIdentifier, String itemIdentifier) throws Exception {
337         //
338         // Create a URN short ID specifier for the getting a copy of the remote authority item
339         //
340         Specifier authoritySpecifier = Specifier.getSpecifier(parentIdentifier);
341         Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier);
342         AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(authoritySpecifier, itemSpecifier);
343         //
344         // Get the remote payload
345         //
346         PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadIn(sasAuthorityItemSpecifier, 
347                         ctx.getServiceName(), getEntityResponseType());
348         //
349         // Using the payload from the remote server, create a local copy of the item
350         //
351         AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
352         Response response = authorityResource.createAuthorityItemWithParentContext(ctx, authoritySpecifier.getURNValue(),
353                         sasPayloadIn, AuthorityServiceUtils.DONT_UPDATE_REV, AuthorityServiceUtils.NOT_PROPOSED);
354         //
355         // Check the response for successful POST result
356         //
357         if (response.getStatus() != Response.Status.CREATED.getStatusCode()) {
358                 throw new DocumentException(String.format("Could not create new authority item '%s' during synchronization of the '%s' authority.",
359                                 itemIdentifier, parentIdentifier));
360         }
361         //
362         // Handle the workflow state
363         //
364         authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier, 
365                         WorkflowClient.WORKFLOWTRANSITION_LOCK, AuthorityServiceUtils.DONT_UPDATE_REV);
366         }
367     
368     /**
369      * Try to synchronize a remote item (using its refName) with a local item.  If the local doesn't yet
370      * exist, we'll create it.
371      * Result values:
372      *  -1 = sync not needed; i.e., already in sync
373      *   0 = sync succeeded
374      *   1 = local item was missing so we created it
375      * @param ctx
376      * @param refName
377      * @return
378      * @throws Exception
379      */
380     protected long syncRemoteItemWithLocalItem(ServiceContext ctx, String itemRefName) throws Exception {
381         long result = -1;
382         //
383         // Using the item refname (with no local CSID), create specifiers that we'll use to find the local versions
384         //
385         AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(itemRefName);
386         String parentIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name);
387         String itemIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.name);
388         //
389         // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
390         //
391         AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();            
392         PoxPayloadOut localItemPayloadOut;
393         try {
394                 localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
395         } catch (DocumentNotFoundException dnf) {
396                 //
397                 // Document not found, means we need to create an item/term that exists only on the SAS
398                 //
399                 logger.info(String.format("Remote item with refname='%s' doesn't exist locally, so we'll create it.", itemRefName));
400                 createLocalItem(ctx, parentIdentifier, itemIdentifier);
401                 return 1; // exit with status of 1 means we created a new authority item
402         }
403         //
404         // If we get here, we know the item exists both locally and remotely, so we need to synchronize them
405         //
406         //
407         try {
408                 PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
409                 if (theUpdate != null) {
410                         result = 0; // means we needed to sync this item with SAS
411                         logger.debug(String.format("Sync'd authority item parent='%s' id='%s with SAS.  Updated payload is: \n%s",
412                                         parentIdentifier, itemIdentifier, theUpdate.getXmlPayload()));
413                 }
414         } catch (DocumentException de) {
415                 cow();
416         }
417         
418         return result; // -1 = no sync needed, 0 = sync'd, 1 = created new item
419     }
420         
421     private PoxPayloadIn requestPayloadInItemList(ServiceContext ctx, Specifier specifier) throws Exception {
422         PoxPayloadIn result = null;
423         
424         AuthorityClient client = (AuthorityClient) ctx.getClient();
425         Response res = client.readItemList(specifier.getURNValue(),
426                         null,   // partial term string
427                         null    // keyword string
428                         );
429         try {
430                 int statusCode = res.getStatus();
431         
432                 // Check the status code of the response: does it match
433                 // the expected response(s)?
434                 if (logger.isDebugEnabled()) {
435                     logger.debug(client.getClass().getCanonicalName() + ": status = " + statusCode);
436                 }
437                 
438             result = new PoxPayloadIn((String)res.readEntity(getEntityResponseType())); // Get the entire response!             
439         } finally {
440                 res.close();
441         }
442         
443         return result;
444     }
445     
446
447     /*
448      * Non standard injection of CSID into common part, since caller may access through
449      * shortId, and not know the CSID.
450      * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
451      */
452     @Override
453     protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
454             throws Exception {
455         Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
456
457         // Add the CSID to the common part
458         if (partMeta.getLabel().equalsIgnoreCase(authorityCommonSchemaName)) {
459             String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
460             unQObjectProperties.put("csid", csid);
461         }
462
463         return unQObjectProperties;
464     }
465     
466     public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
467         super.fillAllParts(wrapDoc, action);
468         //
469         // Update the record's revision number on both CREATE and UPDATE actions, but not on SYNC
470         //
471         if (this.getShouldUpdateRevNumber() == true) { // We won't update rev numbers on synchronization with SAS
472                 updateRevNumbers(wrapDoc);
473         }
474     }
475     
476     protected void updateRevNumbers(DocumentWrapper<DocumentModel> wrapDoc) {
477         DocumentModel documentModel = wrapDoc.getWrappedObject();
478         Long rev = (Long)documentModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV);
479         if (rev == null) {
480                 rev = (long)0;
481         } else {
482                 rev++;
483         }
484         documentModel.setProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV, rev);
485     }
486     
487     /*
488      * We consider workflow state changes as changes that should bump the revision number
489      * (non-Javadoc)
490      * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#handleWorkflowTransition(org.collectionspace.services.common.document.DocumentWrapper, org.collectionspace.services.lifecycle.TransitionDef)
491      */
492     @Override
493     public void handleWorkflowTransition(DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) throws Exception {
494         // Update the revision number
495         updateRevNumbers(wrapDoc);
496     }
497     
498     @Override
499     public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
500         super.handleCreate(wrapDoc);
501         // CSPACE-3178:
502         // Uncomment once debugged and App layer is read to integrate
503         // Experimenting with this uncommented now ...
504         handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityCommonSchemaName);
505         updateRefnameForAuthority(wrapDoc, authorityCommonSchemaName);//CSPACE-3178
506     }
507     
508     protected String buildWhereForShortId(String name) {
509         return authorityCommonSchemaName
510                 + ":" + AuthorityJAXBSchema.SHORT_IDENTIFIER
511                 + "='" + name + "'";
512     }
513     
514     private boolean isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
515         return true;
516     }
517     
518     private boolean temp_isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
519         boolean result = true;
520         
521         ServiceContext ctx = this.getServiceContext();
522         String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
523         String nxqlWhereClause = buildWhereForShortId(shortIdentifier);
524         try {
525                         DocumentWrapper<DocumentModel> searchResultWrapper = getRepositoryClient(ctx).findDoc(ctx, nxqlWhereClause);
526                         if (searchResultWrapper != null) {
527                                 result = false;
528                                 if (logger.isInfoEnabled() == true) {
529                                         DocumentModel searchResult = searchResultWrapper.getWrappedObject();
530                                         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'",
531                                                         shortIdentifier, searchResult.getName());
532                                         logger.trace(debugMsg);
533                                 }
534                         }
535                 } catch (DocumentNotFoundException e) {
536                         // Not a problem, just means we couldn't find another authority with that short ID
537                 }
538         
539         return result;
540     }
541
542     /**
543      * If no short identifier was provided in the input payload,
544      * generate a short identifier from the display name. Either way though,
545      * the short identifier needs to be unique.
546      */
547     private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
548         String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
549         String displayName = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.DISPLAY_NAME);
550         String shortDisplayName = "";
551         String generateShortIdentifier = null;
552         if (Tools.isEmpty(shortIdentifier)) {
553                 generateShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(displayName, shortDisplayName);
554             docModel.setProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER, shortIdentifier);
555         }
556         
557         if (isUnique(docModel, schemaName) == false) {
558                 String shortId = generateShortIdentifier == null ? shortIdentifier : generateShortIdentifier;
559                 String errMsgVerb = generateShortIdentifier == null ? "supplied" : "generated";
560                 String errMsg = String.format("The %s short identifier '%s' was not unique, so the new authority could not be created.",
561                                 errMsgVerb, shortId);
562                 throw new DocumentException(errMsg);
563         }
564     }
565  
566     /**
567      * Generate a refName for the authority from the short identifier
568      * and display name.
569      * 
570      * All refNames for authorities are generated.  If a client supplies
571      * a refName, it will be overwritten during create (per this method) 
572      * or discarded during update (per filterReadOnlyPropertiesForPart).
573      * 
574      * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
575      * 
576      */
577     protected void updateRefnameForAuthority(DocumentWrapper<DocumentModel> wrapDoc, String schemaName) throws Exception {
578         DocumentModel docModel = wrapDoc.getWrappedObject();
579         RefName.Authority authority = (Authority) getRefName(getServiceContext(), docModel);
580         String refName = authority.toString();
581         docModel.setProperty(schemaName, AuthorityJAXBSchema.REF_NAME, refName);
582     }
583     
584     @Override
585     public RefName.RefNameInterface getRefName(ServiceContext ctx,
586                 DocumentModel docModel) {
587         RefName.RefNameInterface refname = null;
588
589         try {
590                 String shortIdentifier = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
591                 String displayName = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.DISPLAY_NAME);
592                 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
593                         ctx.getServiceName(),
594                         null,   // Only use shortId form!!!
595                         shortIdentifier,
596                         displayName);
597                 refname = authority;
598         } catch (Exception e) {
599                 logger.error(e.getMessage(), e);
600         }
601         
602         return refname;
603     }
604     
605     @Override
606     protected String getRefnameDisplayName(DocumentWrapper<DocumentModel> docWrapper) {
607         String result = null;
608         
609         DocumentModel docModel = docWrapper.getWrappedObject();
610         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
611         RefName.Authority refname = (RefName.Authority)getRefName(ctx, docModel);
612         result = refname.getDisplayName();
613         
614         return result;
615     }    
616     
617     public String getShortIdentifier(ServiceContext ctx, String authCSID, String schemaName) throws Exception {
618         String shortIdentifier = null;
619         CoreSessionInterface repoSession = null;
620         boolean releaseSession = false;
621
622         RepositoryClientImpl nuxeoRepoClient = (RepositoryClientImpl)this.getRepositoryClient(ctx);
623         try {
624                 repoSession = nuxeoRepoClient.getRepositorySession(ctx);
625             DocumentWrapper<DocumentModel> wrapDoc = nuxeoRepoClient.getDocFromCsid(ctx, repoSession, authCSID);
626             DocumentModel docModel = wrapDoc.getWrappedObject();
627             if (docModel == null) {
628                 throw new DocumentNotFoundException(String.format("Could not find authority resource with CSID='%s'.", authCSID));
629             }
630             shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
631         } catch (ClientException ce) {
632             throw new RuntimeException("AuthorityDocHandler Internal Error: cannot get shortId!", ce);
633         } finally {
634                 if (repoSession != null) {
635                         nuxeoRepoClient.releaseRepositorySession(ctx, repoSession);
636                 }
637         }
638         
639         return shortIdentifier;
640     }
641
642     /**
643      * Filters out selected values supplied in an update request.
644      * 
645      * @param objectProps the properties filtered out from the update payload
646      * @param partMeta metadata for the object to fill
647      */
648     @Override
649     public void filterReadOnlyPropertiesForPart(
650             Map<String, Object> objectProps, ObjectPartType partMeta) {
651         super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
652         String commonPartLabel = getServiceContext().getCommonPartLabel();
653         if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
654             objectProps.remove(AuthorityJAXBSchema.CSID);
655             objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
656             objectProps.remove(AuthorityJAXBSchema.REF_NAME);
657         }
658     }    
659 }