]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
d6e857432988c3410daabbbf4309e985882b364e
[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         Long localRev = (Long) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REV);
138         String shortId = (String) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
139         //
140         // Using the short ID of the local authority, create a URN specifier to retrieve the SAS authority
141         //
142         Specifier sasSpecifier = new Specifier(SpecifierForm.URN_NAME, shortId);
143         PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadIn(ctx, sasSpecifier, getEntityResponseType());
144         //
145         // If the authority on the SAS is newer, synch all the items and then the authority record as well
146         //
147         //
148         Long sasRev = getRevision(sasPayloadIn);
149         if (sasRev > localRev) {
150                 //
151                 // First, sync all the authority items
152                 //
153                 syncAllItems(ctx, sasSpecifier);
154                 //
155                 // Next, sync the authority resource/record itself
156                 //
157                 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
158                 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
159                 PoxPayloadOut payloadOut = authorityResource.update(ctx, ctx.getResourceMap(), ctx.getUriInfo(), docModel.getName(), 
160                                 sasPayloadIn);
161                 if (payloadOut != null) {
162                         ctx.setOutput(payloadOut);
163                         result = true;
164                 }
165                 //
166                 // We may need to transition the authority into a replicated state the first time we sync it.
167                 //
168                 String workflowState = docModel.getCurrentLifeCycleState();
169                 if (workflowState.contains(WorkflowClient.WORKFLOWSTATE_REPLICATED) == false) {
170                     String authorityCsid = docModel.getName();
171                         authorityResource.updateWorkflowWithTransition(ctx, ctx.getUriInfo(), authorityCsid, WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
172                 }
173         }
174         
175         return result;
176     }
177     
178     /*
179      * Get the list of authority items from the remote shared authority server (SAS) and try
180      * to synchronize them with the local items.  If items exist on the remote but not the local, we'll create them.
181      */
182     protected int syncAllItems(ServiceContext ctx, Specifier sasAuthoritySpecifier) throws Exception {
183         int result = -1;
184         int created = 0;
185         int synched = 0;
186         int alreadySynched = 0;
187         int deprecated = 0;
188         int totalItemsProcessed = 0;
189         ArrayList<String> itemsInRemoteAuthority = new ArrayList<String>();
190         //
191         // Iterate over the list of items/terms in the remote authority
192         //
193         PoxPayloadIn sasPayloadInItemList = requestPayloadInItemList(ctx, sasAuthoritySpecifier);
194         List<Element> itemList = getItemList(sasPayloadInItemList);
195         if (itemList != null) {
196                 for (Element e:itemList) {
197                         String remoteRefName = XmlTools.getElementValue(e, AuthorityItemJAXBSchema.REF_NAME);
198                         itemsInRemoteAuthority.add(XmlTools.getElementValue(e, AuthorityItemJAXBSchema.SHORT_IDENTIFIER));
199                         long status = syncRemoteItemWithLocalItem(ctx, remoteRefName);
200                         if (status == 1) {
201                                 created++;
202                         } else if (status == 0) {
203                                 synched++;
204                         } else {
205                                 alreadySynched++;
206                         }
207                         totalItemsProcessed++;
208                 }
209         }
210         //
211         // Now see if we need to deprecate or delete items that have been hard-deleted from the SAS but still exist
212         // locally.  Subtract (remove) the list of remote items from the list of local items to determine which
213         // of the remote items have been hard deleted.
214         //
215         ArrayList<String> itemsInLocalAuthority = getItemsInLocalAuthority(ctx, sasAuthoritySpecifier);
216         itemsInLocalAuthority.removeAll(itemsInRemoteAuthority);
217         if (itemsInLocalAuthority.size() > 0) {
218                 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)
219                 //
220                 // We now need to either hard-delete or deprecate the remaining authorities
221                 //
222                 long processed = deleteOrDeprecateItems(ctx, sasAuthoritySpecifier, remainingItems);
223                 if (processed != remainingItems.size()) {
224                         throw new Exception("Encountered unexpected exception trying to delete or deprecated authority items during synchronization.");
225                 }
226         }
227         //
228         // Now that we've sync'd all the items, we need to synchronize the hierarchy relationships
229         //
230         for (String itemShortId:itemsInRemoteAuthority) {
231                 long status = syncRemoteItemRelationshipsWithLocalItem(ctx, sasAuthoritySpecifier, itemShortId);
232                 if (status == 1) {
233                         created++;
234                 } else if (status == 0) {
235                         synched++;
236                 } else {
237                         alreadySynched++;
238                 }
239                 totalItemsProcessed++;
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     @SuppressWarnings("rawtypes")
258         private long deleteOrDeprecateItems(ServiceContext ctx, Specifier authoritySpecifier, ArrayList<String> itemShortIdList) throws Exception {
259         long result = 0;
260         AuthorityItemSpecifier authorityItemSpecificer = null;
261
262         ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, false); // Don't update the revision number when we delete or deprecate the item
263         for (String itemShortId:itemShortIdList) {
264                 AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
265                 try {
266                 authorityItemSpecificer = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authoritySpecifier.value,
267                                 itemShortId);
268                         authorityResource.deleteAuthorityItem(ctx,
269                                         authorityItemSpecificer.getParentSpecifier().getURNValue(),
270                                         authorityItemSpecificer.getItemSpecifier().getURNValue(),
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 with '%s' has existing references and cannot be removed during sync.",
275                                         authorityItemSpecificer), 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'", authorityItemSpecificer), e);
283                         throw e;
284                 }
285         }
286
287         if (logger.isWarnEnabled() == true) {
288                 if (result != itemShortIdList.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, itemShortIdList.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, Boolean syncHierarchicalRelationships) 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(), syncHierarchicalRelationships);
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, AuthorityClient.DONT_INCLUDE_RELATIONS);
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, false);
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, Specifier authoritySpecifier, String itemShortId) throws Exception {
448         long result = -1;
449         
450         String parentIdentifier = authoritySpecifier.getURNValue();
451         String itemIdentifier = Specifier.createShortIdURNValue(itemShortId);
452         //
453         // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
454         //
455         AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();            
456         PoxPayloadOut localItemPayloadOut;
457         try {
458                 MultivaluedMap queryParams = ctx.getQueryParams();
459                 localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier);
460         } catch (DocumentNotFoundException dnf) {
461                 //
462                 // Document not found, means we need to create an item/term that exists only on the SAS
463                 //
464                 logger.info(String.format("Remote item with short ID ='%s' doesn't exist locally, so we can't synchronize its relationships.", itemShortId));
465                 return result;
466         }
467         //
468         // If we get here, we know the item exists both locally and remotely, so we need to synchronize the hierarchy relationships.
469         //
470         //
471         try {
472                 PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, AuthorityClient.INCLUDE_RELATIONS);
473                 if (theUpdate != null) {
474                         result = 0; // means we needed to sync this item with SAS
475                         logger.debug(String.format("Sync'd authority item parent='%s' id='%s with SAS.  Updated payload is: \n%s",
476                                         parentIdentifier, itemIdentifier, theUpdate.getXmlPayload()));
477                 }
478         } catch (DocumentReferenceException de) { // Exception for items that still have records/resource referencing them.
479                 result = -1;
480                 logger.error(String.format("Could not sync authority item = '%s' because it has existing records referencing it.",
481                                 itemIdentifier));
482         }
483         
484         return result; // -1 = no sync needed/possible, 0 = sync'd, 1 = created new item
485         
486         
487     }    
488     
489     /**
490      * Request an authority item list payload from the SAS server.
491      * 
492      * @param ctx
493      * @param specifier
494      * @return
495      * @throws Exception
496      */
497     private PoxPayloadIn requestPayloadInItemList(ServiceContext ctx, Specifier specifier) throws Exception {
498         PoxPayloadIn result = null;
499         
500         AuthorityClient client = (AuthorityClient) ctx.getClient();
501         Response res = client.readItemList(specifier.getURNValue(),
502                         null,   // partial term string
503                         null    // keyword string
504                         );
505         try {
506                 int statusCode = res.getStatus();
507         
508                 // Check the status code of the response: does it match
509                 // the expected response(s)?
510                 if (logger.isDebugEnabled()) {
511                     logger.debug(client.getClass().getCanonicalName() + ": status = " + statusCode);
512                 }
513                 
514             result = new PoxPayloadIn((String)res.readEntity(getEntityResponseType())); // Get the entire response!             
515         } finally {
516                 res.close();
517         }
518         
519         return result;
520     }
521     
522
523     /*
524      * Non standard injection of CSID into common part, since caller may access through
525      * shortId, and not know the CSID.
526      * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#extractPart(org.nuxeo.ecm.core.api.DocumentModel, java.lang.String, org.collectionspace.services.common.service.ObjectPartType)
527      */
528     @Override
529     protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
530             throws Exception {
531         Map<String, Object> unQObjectProperties = super.extractPart(docModel, schema, partMeta);
532
533         // Add the CSID to the common part
534         if (partMeta.getLabel().equalsIgnoreCase(authorityCommonSchemaName)) {
535             String csid = getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
536             unQObjectProperties.put("csid", csid);
537         }
538
539         return unQObjectProperties;
540     }
541     
542     public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
543         super.fillAllParts(wrapDoc, action);
544         //
545         // Update the record's revision number on both CREATE and UPDATE actions, but not on SYNC
546         //
547         if (this.getShouldUpdateRevNumber() == true) { // We won't update rev numbers on synchronization with SAS
548                 updateRevNumbers(wrapDoc);
549         }
550     }
551     
552     protected void updateRevNumbers(DocumentWrapper<DocumentModel> wrapDoc) {
553         DocumentModel documentModel = wrapDoc.getWrappedObject();
554         Long rev = (Long)documentModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV);
555         if (rev == null) {
556                 rev = (long)0;
557         } else {
558                 rev++;
559         }
560         documentModel.setProperty(authorityCommonSchemaName, AuthorityJAXBSchema.REV, rev);
561     }
562     
563     /*
564      * We consider workflow state changes as changes that should bump the revision number
565      * (non-Javadoc)
566      * @see org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl#handleWorkflowTransition(org.collectionspace.services.common.document.DocumentWrapper, org.collectionspace.services.lifecycle.TransitionDef)
567      */
568     @Override
569     public void handleWorkflowTransition(ServiceContext ctx, DocumentWrapper<DocumentModel> wrapDoc, TransitionDef transitionDef) throws Exception {
570         boolean updateRevNumber = this.getShouldUpdateRevNumber();
571         Boolean contextProperty = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
572         if (contextProperty != null) {
573                 updateRevNumber = contextProperty;
574         }
575
576         if (updateRevNumber == true) { // We don't update the rev number of synchronization requests
577                 updateRevNumbers(wrapDoc);
578         }
579     }
580     
581     @Override
582     public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
583         super.handleCreate(wrapDoc);
584         // CSPACE-3178:
585         // Uncomment once debugged and App layer is read to integrate
586         // Experimenting with this uncommented now ...
587         handleDisplayNameAsShortIdentifier(wrapDoc.getWrappedObject(), authorityCommonSchemaName);
588         updateRefnameForAuthority(wrapDoc, authorityCommonSchemaName);//CSPACE-3178
589     }
590     
591     protected String buildWhereForShortId(String name) {
592         return authorityCommonSchemaName
593                 + ":" + AuthorityJAXBSchema.SHORT_IDENTIFIER
594                 + "='" + name + "'";
595     }
596     
597     private boolean isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
598         return true;
599     }
600     
601     private boolean temp_isUnique(DocumentModel docModel, String schemaName) throws DocumentException {
602         boolean result = true;
603         
604         ServiceContext ctx = this.getServiceContext();
605         String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
606         String nxqlWhereClause = buildWhereForShortId(shortIdentifier);
607         try {
608                         DocumentWrapper<DocumentModel> searchResultWrapper = getRepositoryClient(ctx).findDoc(ctx, nxqlWhereClause);
609                         if (searchResultWrapper != null) {
610                                 result = false;
611                                 if (logger.isInfoEnabled() == true) {
612                                         DocumentModel searchResult = searchResultWrapper.getWrappedObject();
613                                         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'",
614                                                         shortIdentifier, searchResult.getName());
615                                         logger.trace(debugMsg);
616                                 }
617                         }
618                 } catch (DocumentNotFoundException e) {
619                         // Not a problem, just means we couldn't find another authority with that short ID
620                 }
621         
622         return result;
623     }
624
625     /**
626      * If no short identifier was provided in the input payload,
627      * generate a short identifier from the display name. Either way though,
628      * the short identifier needs to be unique.
629      */
630     private void handleDisplayNameAsShortIdentifier(DocumentModel docModel, String schemaName) throws Exception {
631         String shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
632         String displayName = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.DISPLAY_NAME);
633         String shortDisplayName = "";
634         String generateShortIdentifier = null;
635         if (Tools.isEmpty(shortIdentifier)) {
636                 generateShortIdentifier = AuthorityIdentifierUtils.generateShortIdentifierFromDisplayName(displayName, shortDisplayName);
637             docModel.setProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER, shortIdentifier);
638         }
639         
640         if (isUnique(docModel, schemaName) == false) {
641                 String shortId = generateShortIdentifier == null ? shortIdentifier : generateShortIdentifier;
642                 String errMsgVerb = generateShortIdentifier == null ? "supplied" : "generated";
643                 String errMsg = String.format("The %s short identifier '%s' was not unique, so the new authority could not be created.",
644                                 errMsgVerb, shortId);
645                 throw new DocumentException(errMsg);
646         }
647     }
648  
649     /**
650      * Generate a refName for the authority from the short identifier
651      * and display name.
652      * 
653      * All refNames for authorities are generated.  If a client supplies
654      * a refName, it will be overwritten during create (per this method) 
655      * or discarded during update (per filterReadOnlyPropertiesForPart).
656      * 
657      * @see #filterReadOnlyPropertiesForPart(Map<String, Object>, org.collectionspace.services.common.service.ObjectPartType)
658      * 
659      */
660     protected void updateRefnameForAuthority(DocumentWrapper<DocumentModel> wrapDoc, String schemaName) throws Exception {
661         DocumentModel docModel = wrapDoc.getWrappedObject();
662         RefName.Authority authority = (Authority) getRefName(getServiceContext(), docModel);
663         String refName = authority.toString();
664         docModel.setProperty(schemaName, AuthorityJAXBSchema.REF_NAME, refName);
665     }
666     
667     @Override
668     public RefName.RefNameInterface getRefName(ServiceContext ctx,
669                 DocumentModel docModel) {
670         RefName.RefNameInterface refname = null;
671
672         try {
673                 String shortIdentifier = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
674                 String displayName = (String) docModel.getProperty(authorityCommonSchemaName, AuthorityJAXBSchema.DISPLAY_NAME);
675                 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
676                         ctx.getServiceName(),
677                         null,   // Only use shortId form!!!
678                         shortIdentifier,
679                         displayName);
680                 refname = authority;
681         } catch (Exception e) {
682                 logger.error(e.getMessage(), e);
683         }
684         
685         return refname;
686     }
687     
688     @Override
689     protected String getRefnameDisplayName(DocumentWrapper<DocumentModel> docWrapper) {
690         String result = null;
691         
692         DocumentModel docModel = docWrapper.getWrappedObject();
693         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
694         RefName.Authority refname = (RefName.Authority)getRefName(ctx, docModel);
695         result = refname.getDisplayName();
696         
697         return result;
698     }    
699     
700     public String getShortIdentifier(ServiceContext ctx, String authCSID, String schemaName) throws Exception {
701         String shortIdentifier = null;
702         CoreSessionInterface repoSession = null;
703         boolean releaseSession = false;
704
705         RepositoryClientImpl nuxeoRepoClient = (RepositoryClientImpl)this.getRepositoryClient(ctx);
706         try {
707                 repoSession = nuxeoRepoClient.getRepositorySession(ctx);
708             DocumentWrapper<DocumentModel> wrapDoc = nuxeoRepoClient.getDocFromCsid(ctx, repoSession, authCSID);
709             DocumentModel docModel = wrapDoc.getWrappedObject();
710             if (docModel == null) {
711                 throw new DocumentNotFoundException(String.format("Could not find authority resource with CSID='%s'.", authCSID));
712             }
713             shortIdentifier = (String) docModel.getProperty(schemaName, AuthorityJAXBSchema.SHORT_IDENTIFIER);
714         } catch (ClientException ce) {
715             throw new RuntimeException("AuthorityDocHandler Internal Error: cannot get shortId!", ce);
716         } finally {
717                 if (repoSession != null) {
718                         nuxeoRepoClient.releaseRepositorySession(ctx, repoSession);
719                 }
720         }
721         
722         return shortIdentifier;
723     }
724
725     /**
726      * Filters out selected values supplied in an update request.
727      * 
728      * @param objectProps the properties filtered out from the update payload
729      * @param partMeta metadata for the object to fill
730      */
731     @Override
732     public void filterReadOnlyPropertiesForPart(
733             Map<String, Object> objectProps, ObjectPartType partMeta) {
734         super.filterReadOnlyPropertiesForPart(objectProps, partMeta);
735         String commonPartLabel = getServiceContext().getCommonPartLabel();
736         if (partMeta.getLabel().equalsIgnoreCase(commonPartLabel)) {
737             objectProps.remove(AuthorityJAXBSchema.CSID);
738             objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
739             objectProps.remove(AuthorityJAXBSchema.REF_NAME);
740         }
741     }    
742 }