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