]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
7e24bd9017396e40dd19daa639d57d5beb5b869d
[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;
25
26 import java.util.List;
27 import java.util.Map;
28
29 import javax.ws.rs.Consumes;
30 import javax.ws.rs.DELETE;
31 import javax.ws.rs.GET;
32 import javax.ws.rs.POST;
33 import javax.ws.rs.PUT;
34 import javax.ws.rs.Path;
35 import javax.ws.rs.PathParam;
36 import javax.ws.rs.Produces;
37 import javax.ws.rs.core.Context;
38 import javax.ws.rs.core.MultivaluedMap;
39 import javax.ws.rs.core.Request;
40 import javax.ws.rs.core.Response;
41 import javax.ws.rs.core.UriBuilder;
42 import javax.ws.rs.core.UriInfo;
43 import javax.ws.rs.core.Response.ResponseBuilder;
44
45 import org.collectionspace.services.client.IClientQueryParams;
46 import org.collectionspace.services.client.IQueryManager;
47 import org.collectionspace.services.client.PoxPayload;
48 import org.collectionspace.services.client.PoxPayloadIn;
49 import org.collectionspace.services.client.PoxPayloadOut;
50 import org.collectionspace.services.client.XmlTools;
51 import org.collectionspace.services.client.workflow.WorkflowClient;
52
53 import org.collectionspace.services.common.CSWebApplicationException;
54 import org.collectionspace.services.common.NuxeoBasedResource;
55 import org.collectionspace.services.common.ResourceMap;
56 import org.collectionspace.services.common.ServiceMain;
57 import org.collectionspace.services.common.ServiceMessages;
58 import org.collectionspace.services.common.StoredValuesUriTemplate;
59 import org.collectionspace.services.common.UriInfoWrapper;
60 import org.collectionspace.services.common.UriTemplateFactory;
61 import org.collectionspace.services.common.UriTemplateRegistryKey;
62 import org.collectionspace.services.common.api.RefName;
63 import org.collectionspace.services.common.api.Tools;
64 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
65 import org.collectionspace.services.common.authorityref.AuthorityRefList;
66 import org.collectionspace.services.common.context.JaxRsContext;
67 import org.collectionspace.services.common.context.MultipartServiceContext;
68 import org.collectionspace.services.common.context.RemoteServiceContext;
69 import org.collectionspace.services.common.context.ServiceBindingUtils;
70 import org.collectionspace.services.common.context.ServiceContext;
71 import org.collectionspace.services.common.document.DocumentException;
72 import org.collectionspace.services.common.document.DocumentFilter;
73 import org.collectionspace.services.common.document.DocumentHandler;
74 import org.collectionspace.services.common.document.DocumentNotFoundException;
75 import org.collectionspace.services.common.document.DocumentReferenceException;
76 import org.collectionspace.services.common.document.DocumentWrapper;
77 import org.collectionspace.services.common.document.Hierarchy;
78 import org.collectionspace.services.common.query.QueryManager;
79 import org.collectionspace.services.common.repository.RepositoryClient;
80 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
81 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
82 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
83 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
84 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
85
86 import org.collectionspace.services.config.ClientType;
87 import org.collectionspace.services.config.service.ServiceBindingType;
88 import org.collectionspace.services.jaxb.AbstractCommonList;
89 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
90 import org.collectionspace.services.lifecycle.TransitionDef;
91 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
92 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
93 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentFilter;
94 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
95 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
96 import org.collectionspace.services.workflow.WorkflowCommon;
97 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
98 import org.collectionspace.services.description.ServiceDescription;
99
100 import org.jboss.resteasy.util.HttpResponseCodes;
101 import org.nuxeo.ecm.core.api.DocumentModel;
102 import org.nuxeo.ecm.core.api.DocumentModelList;
103 import org.slf4j.Logger;
104 import org.slf4j.LoggerFactory;
105 import org.w3c.dom.Element;
106
107 /**
108  * The Class AuthorityResource.
109  */
110
111 @SuppressWarnings({"rawtypes", "unchecked"})
112 @Consumes("application/xml")
113 @Produces("application/xml")
114 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
115         extends NuxeoBasedResource {
116     
117     final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
118
119     final static String SEARCH_TYPE_TERMSTATUS = "ts";
120     public final static String hierarchy = "hierarchy";
121     
122     private static final Integer PAGE_NUM_FROM_QUERYPARAMS = null;
123     private static final Integer PAGE_SIZE_FROM_QUERYPARAMS = null;
124
125     protected Class<AuthCommon> authCommonClass;
126     protected Class<?> resourceClass;
127     protected String authorityCommonSchemaName;
128     protected String authorityItemCommonSchemaName;
129     final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed?  I see no references to it.
130         
131     final static String FETCH_SHORT_ID = "_fetch_";
132     public final static String PARENT_WILDCARD = "_ALL_";
133     protected static final boolean DONT_INCLUDE_ITEMS = false;
134     protected static final boolean INCLUDE_ITEMS = true;
135     
136     /**
137      * Instantiates a new Authority resource.
138      */
139     public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
140             String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
141         this.authCommonClass = authCommonClass;
142         this.resourceClass = resourceClass;
143         this.authorityCommonSchemaName = authorityCommonSchemaName;
144         this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
145     }
146
147     public abstract String getItemServiceName();
148     
149     public abstract String getItemTermInfoGroupXPathBase();
150
151     @Override
152     protected String getVersionString() {
153         return "$LastChangedRevision: 2617 $";
154     }
155
156     @Override
157     public Class<AuthCommon> getCommonPartClass() {
158         return authCommonClass;
159     }
160
161     /**
162      * Creates the item document handler.
163      * 
164      * @param ctx the ctx
165      * @param inAuthority the in vocabulary
166      * 
167      * @return the document handler
168      * 
169      * @throws Exception the exception
170      */
171     protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
172             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
173             String inAuthority, String containerShortIdentifier)
174             throws Exception {
175         String authorityRefNameBase;
176         AuthorityItemDocumentModelHandler<?> docHandler;
177
178         if (containerShortIdentifier == null) {
179             authorityRefNameBase = null;
180         } else {
181             ServiceContext<PoxPayloadIn, PoxPayloadOut> containerCtx = createServiceContext(getServiceName());
182             if (containerShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
183                 if (ctx.getCurrentRepositorySession() != null) {
184                     containerCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
185                 }
186                 // Get from parent document
187                 containerShortIdentifier = getAuthShortIdentifier(containerCtx, inAuthority);
188             }
189             authorityRefNameBase = buildAuthorityRefNameBase(containerCtx, containerShortIdentifier);
190         }
191
192         docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
193                 ctx.getCommonPartLabel(getItemServiceName()),
194                 authCommonClass);
195         // FIXME - Richard and Aron think the following three lines should
196         // be in the constructor for the AuthorityItemDocumentModelHandler
197         // because all three are required fields.
198         docHandler.setInAuthority(inAuthority);
199         docHandler.setAuthorityRefNameBase(authorityRefNameBase);
200         docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
201         return docHandler;
202     }
203
204     public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
205             throws DocumentNotFoundException, DocumentException {
206         String shortIdentifier = null;
207         
208         try {
209             AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
210             shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
211         } catch (Exception e) {
212             if (logger.isDebugEnabled()) {
213                 logger.debug("Caught exception ", e);
214             }
215             throw new DocumentException(e);
216         }
217         
218         return shortIdentifier;
219     }
220
221     protected String buildAuthorityRefNameBase(
222             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
223         RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
224                 ctx.getServiceName(), 
225                 null,    // Only use shortId form!!!
226                 shortIdentifier, null);
227         return authority.toString();
228     }
229
230     public static class CsidAndShortIdentifier {
231         String CSID;
232         String shortIdentifier;
233     }
234
235     protected String lookupParentCSID(String parentspecifier, String method,
236             String op, UriInfo uriInfo) throws Exception {
237         CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(NULL_CONTEXT,
238                 parentspecifier, method, op, uriInfo);
239         return tempResult.CSID;
240     }
241     
242     protected String lookupParentCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentspecifier, String method,
243             String op, UriInfo uriInfo) throws Exception {
244         CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
245                 parentspecifier, method, op, uriInfo);
246         return tempResult.CSID;
247     }
248
249
250     private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
251             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx, // Ok to be null
252             String parentIdentifier,
253             String method,
254             String op,
255             UriInfo uriInfo)
256             throws Exception {
257         CsidAndShortIdentifier result = new CsidAndShortIdentifier();
258         Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
259         
260         String parentcsid;
261         String parentShortIdentifier;
262         if (parentSpec.form == SpecifierForm.CSID) {
263             parentShortIdentifier = null;
264             parentcsid = parentSpec.value;
265             // Uncomment when app layer is ready to integrate
266             // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
267             parentShortIdentifier = FETCH_SHORT_ID;
268         } else {
269             parentShortIdentifier = parentSpec.value;
270             String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
271             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
272             CoreSessionInterface repoSession = null;
273             if (existingCtx != null) {
274                 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession();  // We want to use the thread's current repo session
275             }
276             parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
277         }
278         
279         result.CSID = parentcsid;
280         result.shortIdentifier = parentShortIdentifier;
281         
282         return result;
283     }
284
285     public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemspecifier, String parentcsid, String method, String op)
286             throws Exception {
287         String itemcsid;
288         
289         Specifier itemSpec = Specifier.getSpecifier(itemspecifier, method, op);
290         if (itemSpec.form == SpecifierForm.CSID) {
291             itemcsid = itemSpec.value;
292         } else {
293             String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
294             MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
295             CoreSessionInterface repoSession = null;
296             if (existingContext != null) {
297                 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession();  // We want to use the thread's current repo session
298             }
299             itemcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
300         }
301         
302         return itemcsid;
303     }
304
305     /*
306      * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then 
307      * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
308      * Resource. They then call this method on that resource.
309      */
310     @Override
311        public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item) 
312                throws Exception, DocumentNotFoundException {
313         if (item == null) {
314             return null;
315         }
316         String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
317         // Ensure we have the right context.
318         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
319         
320         // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
321         NuxeoRepositoryClientImpl client = (NuxeoRepositoryClientImpl)getRepositoryClient(ctx);
322         String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
323
324         String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
325         ctx = createServiceContext(getItemServiceName());
326         DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
327         DocumentModel docModel = docWrapper.getWrappedObject();
328         return docModel;
329     }
330
331
332     @POST
333     public Response createAuthority(
334             @Context ResourceMap resourceMap,
335             @Context UriInfo uriInfo,
336             String xmlPayload) {
337         //
338         // Requests to create new authorities come in on new threads. Unfortunately, we need to synchronize those threads on this block because, as of 8/27/2015, we can't seem to get Nuxeo
339         // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
340         // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
341         // the code that creates new authorities.  The authority document model handler will first check for authorities with the same short id before
342         // trying to create a new authority.
343         //
344         synchronized(AuthorityResource.class) {
345             try {
346                 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
347                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
348                 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
349                 
350                 String csid = getRepositoryClient(ctx).create(ctx, handler);
351                 UriBuilder path = UriBuilder.fromResource(resourceClass);
352                 path.path("" + csid);
353                 Response response = Response.created(path.build()).build();
354                 return response;
355             } catch (Exception e) {
356                 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
357             }
358         }
359     }
360
361     protected boolean supportsReplicating(String tenantId, String serviceName) {
362         boolean result = false;
363         
364         ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
365         result = sb.isSupportsReplicating();
366         
367         return result;
368     }
369
370     /**
371      * Synchronizes the authority and its items/terms with a Shared Authority Server.
372      * 
373      * @param specifier either a CSID or one of the urn forms
374      * 
375      * @return the authority
376      */
377     @POST
378     @Path("{csid}/sync")
379     public byte[] synchronize(
380             @Context Request request,
381             @Context UriInfo uriInfo,
382             @PathParam("csid") String identifier) {
383         uriInfo = new UriInfoWrapper(uriInfo);
384         byte[] result;
385         boolean neededSync = false;
386         PoxPayloadOut payloadOut = null;
387         Specifier specifier;
388         
389         //
390         // Prevent multiple SAS synchronizations from occurring simultaneously by synchronizing this method.
391         //
392         synchronized(AuthorityResource.class) {        
393             try {
394                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
395                 /*
396                  * Make sure this authority service supports synchronization
397                  */
398                 if (supportsReplicating(ctx.getTenantId(), ctx.getServiceName()) == false) {
399                     throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
400                 }
401                 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
402                 specifier = Specifier.getSpecifier(identifier, "getAuthority", "GET");
403                 handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
404                 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
405                 payloadOut = ctx.getOutput();
406             } catch (Exception e) {
407                 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, identifier);
408             }
409     
410             //
411             // If a sync was needed and was successful, return a copy of the updated resource.  Acts like an UPDATE.
412             //
413             if (neededSync == true) {
414                 result = payloadOut.getBytes();
415             } else {
416                 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
417                         specifier.value).getBytes();
418                 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
419                 throw new CSWebApplicationException(response);
420             }
421         }
422             
423         return result;
424     }
425     
426     /*
427      * Builds a cached JAX-RS response.
428      */
429     protected Response buildResponse(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, PoxPayloadOut payloadOut) {
430         Response result = null;
431         
432         ResponseBuilder responseBuilder = Response.ok(payloadOut.getBytes());
433         this.setCacheControl(ctx, responseBuilder);
434         result = responseBuilder.build();            
435
436         return result;
437     }
438
439     /**
440      * Gets the authority.
441      * 
442      * @param specifier either a CSID or one of the urn forms
443      * 
444      * @return the authority
445      */
446     @GET
447     @Path("{csid}")
448     @Override
449     public Response get(
450             @Context Request request,
451             @Context UriInfo uriInfo,
452             @PathParam("csid") String specifier) {
453         Response result = null;
454         uriInfo = new UriInfoWrapper(uriInfo);
455         
456         try {
457             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(request, uriInfo);
458             PoxPayloadOut payloadout = getAuthority(ctx, request, uriInfo, specifier, DONT_INCLUDE_ITEMS);
459             result = buildResponse(ctx, payloadout);            
460         } catch (Exception e) {
461             throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
462         }
463
464         if (result == null) {
465             Response response = Response.status(Response.Status.NOT_FOUND).entity(
466                     "GET request failed. The requested Authority specifier:" + specifier + ": was not found.").type(
467                     "text/plain").build();
468             throw new CSWebApplicationException(response);
469         }
470
471         return result;
472     }
473         
474     protected PoxPayloadOut getAuthority(
475             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
476             Request request,
477             UriInfo uriInfo,
478             String specifier,
479             boolean includeItems) throws Exception {
480         uriInfo = new UriInfoWrapper(uriInfo);
481         PoxPayloadOut payloadout = null;
482         
483         DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> docHandler = createDocumentHandler(ctx);
484         Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
485         if (spec.form == SpecifierForm.CSID) {
486             if (logger.isDebugEnabled()) {
487                 logger.debug("getAuthority with csid=" + spec.value);
488             }
489             getRepositoryClient(ctx).get(ctx, spec.value, docHandler);
490         } else {
491             String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
492             DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
493             docHandler.setDocumentFilter(myFilter);
494             getRepositoryClient(ctx).get(ctx, docHandler);
495         }
496
497         payloadout = ctx.getOutput();
498         if (includeItems == true) {
499             AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
500             payloadout.addPart(PoxPayload.ABSTRACT_COMMON_LIST_ROOT_ELEMENT_LABEL, itemsList);
501         }
502
503         return payloadout;
504     }    
505
506     /**
507      * Finds and populates the authority list.
508      * 
509      * @param ui the ui
510      * 
511      * @return the authority list
512      */
513     @GET
514     @Produces("application/xml")
515     public AbstractCommonList getAuthorityList(@Context UriInfo uriInfo) { //FIXME - REM 5/3/2012 - This is not reachable from the JAX-RS dispatcher.  Instead the equivalent method in ResourceBase is getting called.
516         uriInfo = new UriInfoWrapper(uriInfo);
517         AbstractCommonList result = null;
518         
519         try {
520             MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
521             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
522                         
523             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
524             DocumentFilter myFilter = handler.getDocumentFilter();
525             // Need to make the default sort order for authority items
526             // be on the displayName field
527             String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
528             if (orderBy == null || orderBy.isEmpty()) {
529                 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
530                         + AuthorityItemJAXBSchema.DISPLAY_NAME;
531                 myFilter.setOrderByClause(qualifiedDisplayNameField);
532             }
533             String nameQ = queryParams.getFirst("refName");
534             if (nameQ != null) {
535                 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
536             }
537             //getRepositoryClient(ctx).getFiltered(ctx, handler); # Something here?
538             String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);            
539             result = search(ctx, handler, uriInfo, orderBy, null, advancedSearch, null);
540             result = handler.getCommonPartList();
541         } catch (Exception e) {
542             throw bigReThrow(e, ServiceMessages.GET_FAILED);
543         }
544         
545         return result;
546     }
547     
548     /**
549      * Overriding this methods to see if we should update the revision number during the update.  We don't
550      * want to update the rev number of synchronization operations.
551      */
552     @Override
553     protected PoxPayloadOut update(String csid,
554             PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
555             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
556             throws Exception {
557         AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
558         Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
559         if (shouldUpdateRev != null) {
560             handler.setShouldUpdateRevNumber(shouldUpdateRev);
561         }
562         getRepositoryClient(ctx).update(ctx, csid, handler);
563         return ctx.getOutput();
564     }
565     
566     /**
567      * Update authority.
568      *
569      * @param specifier the csid or id
570      *
571      * @return the multipart output
572      */
573     @PUT
574     @Path("{csid}")
575     public byte[] updateAuthority(
576             @Context Request request,
577             @Context ResourceMap resourceMap,
578             @Context UriInfo uriInfo,            
579             @PathParam("csid") String specifier,
580             String xmlPayload) {
581         PoxPayloadOut result = null;
582         try {
583             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
584             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
585             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
586             Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
587             String csid = getCsid(ctx, spec);
588             getRepositoryClient(ctx).update(ctx, csid, handler);
589             result = ctx.getOutput();
590         } catch (Exception e) {
591             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
592         }
593         return result.getBytes();
594     }
595     
596     /**
597      * Delete all the items in an authority list.
598      * 
599      * @param specifier
600      * @param uriInfo
601      * @return
602      */
603     @DELETE
604     @Path("{csid}/items")
605     public Response deleteAuthorityItemList(@PathParam("csid") String specifier,
606             @Context UriInfo uriInfo) {
607         uriInfo = new UriInfoWrapper(uriInfo);
608
609         try {
610             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
611             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
612             
613             CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
614             try {
615                 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
616                 //
617                 // Delete all the items one by one
618                 //
619                 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
620                 for (ListItem item : itemsList.getListItem()) {
621                     deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
622                 }
623             } catch (Throwable t) {
624                 repoSession.setTransactionRollbackOnly();
625                 throw t;
626             } finally {
627                 repoClient.releaseRepositorySession(ctx, repoSession);
628             }
629
630             return Response.status(HttpResponseCodes.SC_OK).build();
631         } catch (Exception e) {
632             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
633         }
634     }
635     
636     /**
637      * Delete authority
638      * 
639      * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
640      * 
641      * @return the response
642      */
643     @DELETE
644     @Path("{csid}")
645     public Response deleteAuthority( // # Delete this authority and all of it's items.
646             @Context Request request,
647             @Context UriInfo uriInfo,
648             @PathParam("csid") String specifier) {
649         uriInfo = new UriInfoWrapper(uriInfo);
650
651         if (logger.isDebugEnabled()) {
652             logger.debug("deleteAuthority with specifier=" + specifier);
653         }
654         
655         try {
656             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
657             Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
658             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
659             
660             CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
661             try {
662                 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
663                 //
664                 // First try to delete all the items
665                 //
666                 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
667                 for (ListItem item : itemsList.getListItem()) {
668                     deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
669                 }
670
671                 //
672                 // Lastly, delete the parent/container
673                 //
674                 if (spec.form == SpecifierForm.CSID) {
675                     if (logger.isDebugEnabled()) {
676                         logger.debug("deleteAuthority with csid=" + spec.value);
677                     }
678                     ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
679                     getRepositoryClient(ctx).delete(ctx, spec.value, handler);
680                 } else {
681                     if (logger.isDebugEnabled()) {
682                         logger.debug("deleteAuthority with specifier=" + spec.value);
683                     }                
684                     String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
685                     getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
686                 }
687             } catch (Throwable t) {
688                 repoSession.setTransactionRollbackOnly();
689                 throw t;
690             } finally {
691                 repoClient.releaseRepositorySession(ctx, repoSession);
692             }
693
694             return Response.status(HttpResponseCodes.SC_OK).build();
695         } catch (Exception e) {
696             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
697         }
698     }
699     
700     protected String getCsid(ListItem item) {
701         String result = null;
702         
703         for (Element ele : item.getAny()) {
704             String elementName = ele.getTagName().toLowerCase();
705             if (elementName.equals("csid")) {
706                 result = ele.getTextContent();
707                 break;
708             }
709         }
710         
711         return result;
712     }
713
714     /**
715      * 
716      * @param ctx
717      * @param parentspecifier        - ID of the container. Can be URN or CSID form
718      * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
719      * @param isProposed                - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
720      * @return
721      * @throws Exception
722      */
723     protected Response createAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentIdentifier,
724             boolean shouldUpdateRevNumber,
725             boolean isProposed,
726             boolean isSasItem) throws Exception {
727         Response result = null;
728         
729         // Note: must have the parentShortId, to do the create.
730         CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
731         AuthorityItemDocumentModelHandler handler = 
732             (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
733         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
734         handler.setIsProposed(isProposed);
735         handler.setIsSASItem(isSasItem);
736         // Make the client call
737         String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
738         
739         // Build the JAX-RS response
740         UriBuilder path = UriBuilder.fromResource(resourceClass);
741         path.path(parent.CSID + "/items/" + itemcsid);
742         result = Response.created(path.build()).build();
743
744         return result;
745     }
746     
747     public PoxPayloadOut updateAuthorityItem(
748             ServiceContext<PoxPayloadIn, PoxPayloadOut> itemServiceCtx, // Ok to be null.  Will be null on PUT calls, but not on sync calls
749             ResourceMap resourceMap, 
750             UriInfo uriInfo,
751             String parentspecifier,
752             String itemspecifier,
753             PoxPayloadIn theUpdate,
754             boolean shouldUpdateRevNumber,
755             Boolean isProposed,
756             Boolean isSASItem
757             ) throws Exception {
758         PoxPayloadOut result = null;
759         
760         CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
761         String parentcsid = csidAndShortId.CSID;
762         String parentShortId = csidAndShortId.shortIdentifier;
763         //
764         // If the itemServiceCtx context is not null, use it.  Otherwise, create a new context
765         //
766         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
767         if (ctx == null) {
768             ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
769         } else {
770             ctx.setInput(theUpdate); // the update payload
771         }
772         
773         String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
774
775         // We omit the parentShortId, only needed when doing a create...
776         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
777         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
778         //
779         // Update the SAS fields if either value is non-null
780         //
781         boolean updateSASFields = isProposed != null || isSASItem != null;
782         handler.setshouldUpdateSASFields(updateSASFields);
783         if (updateSASFields == true) {
784             handler.setshouldUpdateSASFields(true);
785             if (isProposed != null) {
786                 handler.setIsProposed(isProposed);
787             }
788             if (isSASItem != null) {
789                 handler.setIsSASItem(isSASItem);
790             }
791         }
792         
793         getRepositoryClient(ctx).update(ctx, itemcsid, handler);
794         result = ctx.getOutput();
795
796         return result;
797     }    
798
799     /**
800      * Called with an existing context.
801      * @param parentCtx
802      * @param parentIdentifier
803      * @param input
804      * @return
805      * @throws Exception
806      */
807     public Response createAuthorityItemWithParentContext(ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx,
808             String parentIdentifier,
809             PoxPayloadIn input,
810             boolean shouldUpdateRevNumber,
811             boolean isProposed,
812             boolean isSASItem) throws Exception {
813         Response result = null;
814         
815         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
816                 parentCtx.getResourceMap(), parentCtx.getUriInfo());
817         if (parentCtx.getCurrentRepositorySession() != null) {
818             ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
819         }
820         result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
821
822         return result;
823     }
824         
825     /*************************************************************************
826      * Create an AuthorityItem - this is a sub-resource of Authority
827      * @param specifier either a CSID or one of the urn forms
828      * @return Authority item response
829      *************************************************************************/
830     @POST
831     @Path("{csid}/items")
832     public Response createAuthorityItem(
833             @Context ResourceMap resourceMap,
834             @Context UriInfo uriInfo,
835             @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
836             String xmlPayload) {
837         uriInfo = new UriInfoWrapper(uriInfo);
838         Response result = null;
839         
840         try {
841             PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
842             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
843             result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
844                     AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
845         } catch (Exception e) {
846             throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
847         }
848
849         return result;
850     }
851
852     @GET
853     @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
854     public byte[] getItemWorkflow(
855             @PathParam("csid") String csid,
856             @PathParam("itemcsid") String itemcsid) {
857         PoxPayloadOut result = null;
858
859         try {
860             ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
861             String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
862
863             MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
864             WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
865             ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
866             getRepositoryClient(ctx).get(ctx, itemcsid, handler);
867             result = ctx.getOutput();
868         } catch (Exception e) {
869             throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
870         }
871         return result.getBytes();
872     }
873
874     /*
875      * We should consider changing this code.  The RepositoryClient (from call to getRepositoryClient) could support a call doWorkflowTransition() instead?
876      */
877     @Override
878     @PUT
879     @Path("{csid}" + WorkflowClient.SERVICE_PATH + "/" + "{transition}")
880     public byte[] updateWorkflowWithTransition(
881             @Context UriInfo uriInfo,
882             @PathParam("csid") String specifier,
883             @PathParam("transition") String transition) {
884         PoxPayloadOut result = null;
885                 
886         Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
887         String csid = null;
888         try {
889             csid = getCsid(null, spec);
890             result = updateWorkflowWithTransition(NULL_CONTEXT, uriInfo, csid, transition);
891         } catch (Exception e) {
892             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
893         }
894         
895         return result.getBytes();
896     }
897     
898     //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
899     // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
900     @PUT
901     @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
902     public byte[] updateItemWorkflowWithTransition(
903             @Context UriInfo uriInfo,
904             @PathParam("csid") String parentIdentifier,
905             @PathParam("itemcsid") String itemIdentifier,
906             @PathParam("transition") String transition) {
907         uriInfo = new UriInfoWrapper(uriInfo);
908         PoxPayloadOut result = null;
909         
910         try {
911             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
912             result = updateItemWorkflowWithTransition(ctx, 
913                     parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
914         } catch (Exception e) {
915             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
916         }
917         
918         return result.getBytes();
919     }
920     
921     /**
922      * Update an authority item's workflow state.
923      * @param existingContext
924      * @param csid
925      * @param itemcsid
926      * @param transition
927      * @return
928      * @throws DocumentReferenceException 
929      */
930     public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
931             String parentIdentifier,
932             String itemIdentifier,
933             String transition,
934             boolean updateRevNumber) throws DocumentReferenceException {
935         PoxPayloadOut result = null;
936         
937         try {
938             //
939             // We need CSIDs for both the parent authority and the authority item
940             //
941             CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
942             String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
943
944             //
945             // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
946             //
947             PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(), 
948                     WorkflowClient.SERVICE_COMMONPART_NAME);
949             MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
950             if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
951                 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
952             }
953             //
954             // Create a service context and document handler for the target resource -not the workflow resource itself.
955             //
956             ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
957             AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
958             targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
959             ctx.setProperty(WorkflowClient.TARGET_DOCHANDLER, targetDocHandler); //added as a context param for the workflow document handler -it will call the parent's dochandler "prepareForWorkflowTranstion" method
960             //
961             // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
962             //
963             String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
964             ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
965             
966             // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
967             TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
968             if (transitionDef == null) {
969                 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
970                         itemIdentifier, transition));
971             }
972             ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
973             
974             WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
975             getRepositoryClient(ctx).update(ctx, itemCsid, handler);
976             result = ctx.getOutput();
977         } catch (DocumentReferenceException de) {
978             throw de;
979         } catch (Exception e) {
980             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
981         }
982         
983         return result;
984     }
985     
986     private PoxPayloadOut getAuthorityItem(
987             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
988             String parentIdentifier,
989             String itemIdentifier) throws Exception {
990         PoxPayloadOut result = null;
991         
992         String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
993         // We omit the parentShortId, only needed when doing a create...
994         DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
995
996         Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
997         if (itemSpec.form == SpecifierForm.CSID) {
998             // TODO should we assert that the item is in the passed vocab?
999             getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
1000         } else {
1001             String itemWhereClause =
1002                     RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
1003             DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
1004             handler.setDocumentFilter(myFilter);
1005             getRepositoryClient(ctx).get(ctx, handler);
1006         }
1007         
1008         result = (PoxPayloadOut) ctx.getOutput();
1009         if (result != null) {
1010             String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
1011             if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
1012                 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
1013                         itemSpec.value, inAuthority, parentcsid));
1014             }
1015         }
1016         
1017         return result;
1018     }
1019
1020     public PoxPayloadOut getAuthorityItemWithExistingContext(
1021             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1022             String parentIdentifier,
1023             String itemIdentifier) throws Exception {
1024         PoxPayloadOut result = null;
1025         
1026         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
1027         if (existingCtx.getCurrentRepositorySession() != null) {
1028             ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
1029             ctx.setProperties(existingCtx.getProperties());
1030         }
1031         result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1032         
1033         return result;
1034     }
1035     
1036     /**
1037      * Gets the authority item.
1038      * 
1039      * @param parentspecifier either a CSID or one of the urn forms
1040      * @param itemspecifier either a CSID or one of the urn forms
1041      * 
1042      * @return the authority item
1043      */
1044     @GET
1045     @Path("{csid}/items/{itemcsid}")
1046     public byte[] getAuthorityItem(
1047             @Context Request request,
1048             @Context UriInfo uriInfo,
1049             @Context ResourceMap resourceMap,            
1050             @PathParam("csid") String parentIdentifier,
1051             @PathParam("itemcsid") String itemIdentifier) {
1052         uriInfo = new UriInfoWrapper(uriInfo);
1053         PoxPayloadOut result = null;
1054         try {
1055             RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = 
1056                     (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1057
1058             JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1059             ctx.setJaxRsContext(jaxRsContext);
1060             
1061             result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1062         } catch (DocumentNotFoundException dnf) {
1063             throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1064         } catch (Exception e) {
1065             throw bigReThrow(e, ServiceMessages.GET_FAILED);
1066         }
1067                 
1068         return result.getBytes();
1069     }
1070
1071     /*
1072      * Most of the authority child classes will/should use this implementation.  However, the Vocabulary service's item schema is
1073      * different enough that it will have to override this method in it's resource class.
1074      */
1075     @Override
1076     protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1077         String result = null;
1078
1079         result = NuxeoUtils.getPrimaryElPathPropertyName(
1080                 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1081                 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1082
1083         return result;
1084     }
1085     
1086     @Override
1087     protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1088         String result = null;
1089         
1090         result = NuxeoUtils.getMultiElPathPropertyName(
1091                 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1092                 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1093
1094         return result;
1095     }
1096     
1097     /**
1098      * Gets the authorityItem list for the specified authority
1099      * If partialPerm is specified, keywords will be ignored.
1100      * 
1101      * @param authorityIdentifier either a CSID or one of the urn forms
1102      * @param partialTerm if non-null, matches partial terms
1103      * @param keywords if non-null, matches terms in the keyword index for items
1104      * @param ui passed to include additional parameters, like pagination controls
1105      *
1106      */
1107     public AbstractCommonList getAuthorityItemList(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1108             String authorityIdentifier,
1109             UriInfo uriInfo) throws Exception {
1110         AbstractCommonList result = null;
1111         
1112         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1113         MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1114         if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
1115             ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
1116             ctx.setProperties(existingContext.getProperties());
1117         }
1118             
1119         String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
1120         String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
1121         String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
1122         String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
1123         String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
1124
1125         // For the wildcard case, parentcsid is null, but docHandler will deal with this.
1126         // We omit the parentShortId, only needed when doing a create...
1127         String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
1128             lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
1129         DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
1130             createItemDocumentHandler(ctx, parentcsid, null);
1131         
1132         DocumentFilter myFilter = handler.getDocumentFilter();
1133         // If we are not wildcarding the parent, add a restriction
1134         if (parentcsid != null) {
1135             myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
1136                     + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
1137                     + "'" + parentcsid + "'",
1138                     IQueryManager.SEARCH_QUALIFIER_AND);
1139         }
1140
1141         if (Tools.notBlank(termStatus)) {
1142             // Start with the qualified termStatus field
1143             String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
1144                     + AuthorityItemJAXBSchema.TERM_STATUS;
1145             String[] filterTerms = termStatus.trim().split("\\|");
1146             String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
1147             myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
1148         }
1149
1150         result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);            
1151         
1152         return result;
1153     }
1154     
1155     /**
1156      * Gets the authorityItem list for the specified authority
1157      * If partialPerm is specified, keywords will be ignored.
1158      * 
1159      * @param authorityIdentifier either a CSID or one of the urn forms
1160      * @param partialTerm if non-null, matches partial terms
1161      * @param keywords if non-null, matches terms in the keyword index for items
1162      * @param ui passed to include additional parameters, like pagination controls
1163      * 
1164      * @return the authorityItem list
1165      */
1166     @GET
1167     @Path("{csid}/items")
1168     @Produces("application/xml")
1169     public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
1170             @Context UriInfo uriInfo) {
1171         uriInfo = new UriInfoWrapper(uriInfo);
1172         AbstractCommonList result = null;
1173         
1174         try {
1175             result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);    
1176         } catch (Exception e) {
1177             throw bigReThrow(e, ServiceMessages.LIST_FAILED);
1178         }
1179         
1180         return result;
1181     }
1182
1183     /**
1184      * @return the name of the property used to specify references for items in this type of
1185      * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
1186      * Some types (like Vocabulary) use a separate property.
1187      */
1188     protected String getRefPropName() {
1189         return ServiceBindingUtils.AUTH_REF_PROP;
1190     }
1191     
1192     /**
1193      * Gets the entities referencing this Authority item instance. The service type
1194      * can be passed as a query param "type", and must match a configured type
1195      * for the service bindings. If not set, the type defaults to
1196      * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1197      *
1198      * @param parentspecifier either a CSID or one of the urn forms
1199      * @param itemspecifier either a CSID or one of the urn forms
1200      * @param ui the ui
1201      * 
1202      * @return the info for the referencing objects
1203      */
1204     @GET
1205     @Path("{csid}/items/{itemcsid}/refObjs")
1206     @Produces("application/xml")
1207     public AuthorityRefDocList getReferencingObjects(
1208             @PathParam("csid") String parentSpecifier,
1209             @PathParam("itemcsid") String itemSpecifier,
1210             @Context UriInfo uriInfo) {
1211         uriInfo = new UriInfoWrapper(uriInfo);
1212         AuthorityRefDocList authRefDocList = null;
1213         try {
1214             authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriInfo, PAGE_NUM_FROM_QUERYPARAMS, PAGE_SIZE_FROM_QUERYPARAMS, true, true);
1215         } catch (Exception e) {
1216             throw bigReThrow(e, ServiceMessages.GET_FAILED);
1217         }
1218         
1219         if (authRefDocList == null) {
1220             Response response = Response.status(Response.Status.NOT_FOUND).entity(
1221                     "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1222                     "text/plain").build();
1223             throw new CSWebApplicationException(response);
1224         }
1225         return authRefDocList;
1226     }
1227     
1228     public AuthorityRefDocList getReferencingObjects(
1229             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1230             String parentspecifier,
1231             String itemspecifier,
1232             UriInfo uriInfo,
1233             Integer pageNum,
1234             Integer pageSize,
1235             boolean useDefaultOrderByClause,
1236             boolean computeTotal) throws Exception {
1237         AuthorityRefDocList authRefDocList = null;
1238  
1239         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1240         MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1241         //
1242         // Merge parts of existing context with our new context
1243         //
1244         if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1245             ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());  // If one exists, use the existing repo session
1246             ctx.setProperties(existingContext.getProperties());
1247         }
1248
1249         String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1250         String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1251         
1252         // Remove the "type" property from the query params
1253         List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);        
1254         if (serviceTypes == null || serviceTypes.isEmpty()) {
1255             serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1256         }
1257             
1258         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1259         authRefDocList = handler.getReferencingObjects(ctx, serviceTypes, getRefPropName(), itemcsid, pageNum, pageSize, useDefaultOrderByClause, computeTotal);
1260
1261         return authRefDocList;
1262     }
1263
1264     /**
1265      * Gets the authority terms used in the indicated Authority item.
1266      *
1267      * @param parentspecifier either a CSID or one of the urn forms
1268      * @param itemspecifier either a CSID or one of the urn forms
1269      * @param ui passed to include additional parameters, like pagination controls
1270      *
1271      * @return the authority refs for the Authority item.
1272      */
1273     @GET
1274     @Path("{csid}/items/{itemcsid}/authorityrefs")
1275     @Produces("application/xml")
1276     public AuthorityRefList getAuthorityItemAuthorityRefs(
1277             @PathParam("csid") String parentspecifier,
1278             @PathParam("itemcsid") String itemspecifier,
1279             @Context UriInfo uriInfo) {
1280         uriInfo = new UriInfoWrapper(uriInfo);
1281         AuthorityRefList authRefList = null;
1282         
1283         try {
1284             // Note that we have to create the service context for the Items, not the main service
1285             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1286             String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1287             // We omit the parentShortId, only needed when doing a create...
1288             DocumentModelHandler<?, AbstractCommonList> handler =
1289                     (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1290
1291             String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1292
1293             List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1294             authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1295         } catch (Exception e) {
1296             throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1297         }
1298         
1299         return authRefList;
1300     }
1301     
1302     /**
1303      * Synchronizes a local authority item with a share authority server (SAS) item.
1304      * @param ctx
1305      * @param parentIdentifier
1306      * @param itemIdentifier
1307      * @return
1308      * @throws Exception
1309      */
1310     private PoxPayloadOut synchronizeItem(
1311             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1312             String parentIdentifier,
1313             String itemIdentifier,
1314             boolean syncHierarchicalRelationships) throws Exception {
1315         PoxPayloadOut result = null;
1316         AuthorityItemSpecifier specifier;
1317         boolean neededSync = false;
1318
1319         CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1320         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1321         handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1322         handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1323         handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1324         // Create an authority item specifier
1325         Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1326         Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1327         specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1328         //
1329         neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1330         if (neededSync == true) {
1331             result = (PoxPayloadOut) ctx.getOutput();
1332         }
1333         
1334         return result;
1335     }
1336
1337     /**
1338      * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1339      * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1340      * local authority.  The parent context was created for the authority (parent) because the sync started there.
1341      * @param existingCtx
1342      * @param parentIdentifier
1343      * @param itemIdentifier
1344      * @return
1345      * @throws Exception
1346      */
1347     public PoxPayloadOut synchronizeItemWithExistingContext(
1348             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1349             String parentIdentifier,
1350             String itemIdentifier,
1351             boolean syncHierarchicalRelationships
1352             ) throws Exception {
1353         PoxPayloadOut result = null;
1354         
1355         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1356                 existingCtx.getResourceMap(),
1357                 existingCtx.getUriInfo());
1358         if (existingCtx.getCurrentRepositorySession() != null) {
1359             ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1360             
1361         }
1362         result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1363         
1364         return result;
1365     }
1366     
1367     /**
1368      * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1369      * 
1370      * @param specifier either CSIDs and/or one of the urn forms
1371      * 
1372      * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1373      */
1374     @POST
1375     @Path("{csid}/items/{itemcsid}/sync")
1376     public byte[] synchronizeItem(
1377             @Context ResourceMap resourceMap,
1378             @Context UriInfo uriInfo,
1379             @PathParam("csid") String parentIdentifier,
1380             @PathParam("itemcsid") String itemIdentifier) {
1381         uriInfo = new UriInfoWrapper(uriInfo);
1382         byte[] result;
1383         boolean neededSync = false;
1384         PoxPayloadOut payloadOut = null;
1385         
1386         try {
1387             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1388             payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1389             if (payloadOut != null) {
1390                 neededSync = true;
1391             }
1392         } catch (Exception e) {
1393             throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1394         }
1395
1396         //
1397         // If a sync was needed and was successful, return a copy of the updated resource.  Acts like an UPDATE.
1398         //
1399         if (neededSync == true) {
1400             result = payloadOut.getBytes();
1401         } else {
1402             result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1403                     itemIdentifier).getBytes();
1404             Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1405             throw new CSWebApplicationException(response);
1406         }
1407         
1408         return result;
1409     }
1410     
1411     /**
1412      * Update authorityItem.
1413      * 
1414      * @param parentspecifier either a CSID or one of the urn forms
1415      * @param itemspecifier either a CSID or one of the urn forms
1416      *
1417      * @return the multipart output
1418      */
1419     @PUT
1420     @Path("{csid}/items/{itemcsid}")
1421     public byte[] updateAuthorityItem(
1422             @Context ResourceMap resourceMap, 
1423             @Context UriInfo uriInfo,
1424             @PathParam("csid") String parentSpecifier,
1425             @PathParam("itemcsid") String itemSpecifier,
1426             String xmlPayload) {
1427         uriInfo = new UriInfoWrapper(uriInfo);
1428         PoxPayloadOut result = null;
1429         
1430         try {
1431             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1432             result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1433                     AuthorityServiceUtils.UPDATE_REV,            // passing TRUE so rev num increases, passing
1434                     AuthorityServiceUtils.NO_CHANGE,    // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1435                     AuthorityServiceUtils.NO_CHANGE);    // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1436         } catch (Exception e) {
1437             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1438         }
1439         
1440         return result.getBytes();
1441     }
1442     
1443
1444
1445     /**
1446      * Delete authorityItem.
1447      * 
1448      * @param parentIdentifier the parentcsid
1449      * @param itemIdentifier the itemcsid
1450      * 
1451      * @return the response
1452      */
1453     @DELETE
1454     @Path("{csid}/items/{itemcsid}")
1455     public Response deleteAuthorityItem(
1456             @Context UriInfo uriInfo,
1457             @PathParam("csid") String parentIdentifier,
1458             @PathParam("itemcsid") String itemIdentifier) {
1459         uriInfo = new UriInfoWrapper(uriInfo);
1460         Response result = null;
1461
1462         ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1463         ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1464         if (logger.isDebugEnabled()) {
1465             logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1466         }
1467         
1468         try {
1469             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1470             deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1471             result = Response.status(HttpResponseCodes.SC_OK).build();
1472         } catch (Exception e) {
1473             throw bigReThrow(e, ServiceMessages.DELETE_FAILED + "  itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1474         }
1475
1476         return result;
1477     }
1478
1479     /**
1480      * 
1481      * @param existingCtx
1482      * @param parentIdentifier
1483      * @param itemIdentifier
1484      * @throws Exception
1485      */
1486     public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1487             String parentIdentifier,
1488             String itemIdentifier,
1489             boolean shouldUpdateRevNumber
1490             ) throws Exception {
1491         boolean result = true;
1492         
1493         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1494         if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1495             ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1496             ctx.setProperties(existingCtx.getProperties());
1497         }
1498         
1499         String parentcsid = null;
1500         try {
1501             parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1502         } catch (DocumentNotFoundException de) {
1503             String msg = String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1504                     parentIdentifier, itemIdentifier);
1505             logger.warn(msg);
1506             throw de;
1507         }
1508         String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1509         
1510         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1511         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1512         result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1513         
1514         return result;
1515     }
1516
1517     @GET
1518     @Path("{csid}/items/{itemcsid}/" + hierarchy)
1519     @Produces("application/xml")
1520     public String getHierarchy(
1521             @PathParam("csid") String parentIdentifier,
1522             @PathParam("itemcsid") String itemIdentifier,
1523             @Context UriInfo uriInfo) throws Exception {
1524         uriInfo = new UriInfoWrapper(uriInfo);
1525         String result = null;
1526
1527         try {
1528             //
1529             // All items in dive can look at their child uri's to get uri.  So we calculate the very first one.  We could also do a GET and look at the common part uri field, but why...?
1530             //
1531             String calledUri = uriInfo.getPath();
1532             String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1533             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1534             
1535             String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1536             String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1537             
1538             String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1539             if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1540                 result = Hierarchy.surface(ctx, itemcsid, uri);
1541             } else {
1542                 result = Hierarchy.dive(ctx, itemcsid, uri);
1543             }            
1544         } catch (Exception e) {
1545             throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1546         }
1547         
1548         return result;
1549     }
1550     
1551     /**
1552      * 
1553      * @param tenantId
1554      * @return
1555      */
1556     public String getItemDocType(String tenantId) {
1557         return getDocType(tenantId, getItemServiceName());
1558     }
1559         
1560     /**
1561      * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1562      * for the current resource, for all tenants
1563      * 
1564      * @return a map of URI templates for the current resource, for all tenants
1565      */
1566     @Override
1567     public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1568         Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1569                 super.getUriRegistryEntries();
1570         List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1571         for (String tenantId : tenantIds) {
1572                 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1573         }
1574         return uriRegistryEntriesMap;
1575     }
1576     
1577     /**
1578      * 
1579      */
1580     @Override
1581     public ServiceDescription getDescription(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1582         ServiceDescription result = super.getDescription(ctx);
1583         result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));
1584         return result;
1585     }
1586
1587     public Response createAuthority(String xmlPayload) {
1588         return this.createAuthority(null, null, xmlPayload);
1589     }
1590     
1591     protected String getCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, Specifier specifier) throws Exception {
1592         String csid;
1593         
1594         if (ctx == null) {
1595             ctx = createServiceContext(getServiceName());
1596         }
1597         
1598         if (specifier.form == SpecifierForm.CSID) {
1599             csid = specifier.value;
1600         } else {
1601             String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, specifier.value);
1602             csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
1603         }
1604         
1605         return csid;
1606     }
1607     
1608 }