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