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