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