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