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