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