]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
b29414e992252ded12bb11751d8109dea669379c
[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         return this.getAuthorityList(null, uriInfo);
531     }
532
533     public AbstractCommonList getAuthorityList(ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx, @Context UriInfo uriInfo) {
534         uriInfo = new UriInfoWrapper(uriInfo);
535         AbstractCommonList result = null;
536
537         try {
538             MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
539             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
540             if (parentCtx != null && parentCtx.getCurrentRepositorySession() != null) {
541                 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
542             }
543             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
544             DocumentFilter myFilter = handler.getDocumentFilter();
545             // Need to make the default sort order for authority items
546             // be on the displayName field
547             String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
548             if (orderBy == null || orderBy.isEmpty()) {
549                 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
550                         + AuthorityItemJAXBSchema.DISPLAY_NAME;
551                 myFilter.setOrderByClause(qualifiedDisplayNameField);
552             }
553             String nameQ = queryParams.getFirst("refName");
554             if (nameQ != null) {
555                 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
556             }
557             //getRepositoryClient(ctx).getFiltered(ctx, handler); # Something here?
558             String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
559             result = search(ctx, handler, uriInfo, orderBy, null, advancedSearch, null);
560             result = handler.getCommonPartList();
561         } catch (Exception e) {
562             throw bigReThrow(e, ServiceMessages.GET_FAILED);
563         }
564
565         return result;
566     }
567
568     /**
569      * Overriding this methods to see if we should update the revision number during the update.  We don't
570      * want to update the rev number of synchronization operations.
571      */
572     @Override
573     protected PoxPayloadOut update(String csid,
574             PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
575             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
576             throws Exception {
577         AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
578         Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
579         if (shouldUpdateRev != null) {
580             handler.setShouldUpdateRevNumber(shouldUpdateRev);
581         }
582         getRepositoryClient(ctx).update(ctx, csid, handler);
583         return ctx.getOutput();
584     }
585
586     /**
587      * Update authority.
588      *
589      * @param specifier the csid or id
590      *
591      * @return the multipart output
592      */
593     @PUT
594     @Path("{csid}")
595     public byte[] updateAuthority(
596             @Context Request request,
597             @Context ResourceMap resourceMap,
598             @Context UriInfo uriInfo,
599             @PathParam("csid") String specifier,
600             String xmlPayload) {
601         PoxPayloadOut result = null;
602         try {
603             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
604             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
605             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
606             Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
607             String csid = getCsid(ctx, spec);
608             getRepositoryClient(ctx).update(ctx, csid, handler);
609             result = ctx.getOutput();
610         } catch (Exception e) {
611             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
612         }
613         return result.getBytes();
614     }
615
616     /**
617      * Delete all the items in an authority list.
618      *
619      * @param specifier
620      * @param uriInfo
621      * @return
622      */
623     @DELETE
624     @Path("{csid}/items")
625     public Response deleteAuthorityItemList(@PathParam("csid") String specifier,
626             @Context UriInfo uriInfo) {
627         uriInfo = new UriInfoWrapper(uriInfo);
628
629         try {
630             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
631             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
632
633             CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
634             try {
635                 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
636                 //
637                 // Delete all the items one by one
638                 //
639                 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
640                 for (ListItem item : itemsList.getListItem()) {
641                     deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
642                 }
643             } catch (Throwable t) {
644                 repoSession.setTransactionRollbackOnly();
645                 throw t;
646             } finally {
647                 repoClient.releaseRepositorySession(ctx, repoSession);
648             }
649
650             return Response.status(HttpResponseCodes.SC_OK).build();
651         } catch (Exception e) {
652             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
653         }
654     }
655
656     /**
657      * Delete authority
658      *
659      * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
660      *
661      * @return the response
662      */
663     @DELETE
664     @Path("{csid}")
665     public Response deleteAuthority( // # Delete this authority and all of it's items.
666             @Context Request request,
667             @Context UriInfo uriInfo,
668             @PathParam("csid") String specifier) {
669         uriInfo = new UriInfoWrapper(uriInfo);
670
671         if (logger.isDebugEnabled()) {
672             logger.debug("deleteAuthority with specifier=" + specifier);
673         }
674
675         try {
676             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
677             Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
678             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
679
680             CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
681             try {
682                 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
683                 //
684                 // First try to delete all the items
685                 //
686                 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
687                 for (ListItem item : itemsList.getListItem()) {
688                     deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
689                 }
690
691                 //
692                 // Lastly, delete the parent/container
693                 //
694                 if (spec.form == SpecifierForm.CSID) {
695                     if (logger.isDebugEnabled()) {
696                         logger.debug("deleteAuthority with csid=" + spec.value);
697                     }
698                     ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
699                     getRepositoryClient(ctx).delete(ctx, spec.value, handler);
700                 } else {
701                     if (logger.isDebugEnabled()) {
702                         logger.debug("deleteAuthority with specifier=" + spec.value);
703                     }
704                     String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
705                     getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
706                 }
707             } catch (Throwable t) {
708                 repoSession.setTransactionRollbackOnly();
709                 throw t;
710             } finally {
711                 repoClient.releaseRepositorySession(ctx, repoSession);
712             }
713
714             return Response.status(HttpResponseCodes.SC_OK).build();
715         } catch (Exception e) {
716             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
717         }
718     }
719
720     protected String getCsid(ListItem item) {
721         String result = null;
722
723         for (Element ele : item.getAny()) {
724             String elementName = ele.getTagName().toLowerCase();
725             if (elementName.equals("csid")) {
726                 result = ele.getTextContent();
727                 break;
728             }
729         }
730
731         return result;
732     }
733
734     /**
735      *
736      * @param ctx
737      * @param parentspecifier        - ID of the container. Can be URN or CSID form
738      * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
739      * @param isProposed                - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
740      * @return
741      * @throws Exception
742      */
743     protected Response createAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentIdentifier,
744             boolean shouldUpdateRevNumber,
745             boolean isProposed,
746             boolean isSasItem) throws Exception {
747         Response result = null;
748
749         // Note: must have the parentShortId, to do the create.
750         CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
751         AuthorityItemDocumentModelHandler handler =
752             (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
753         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
754         handler.setIsProposed(isProposed);
755         handler.setIsSASItem(isSasItem);
756         // Make the client call
757         String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
758
759         // Build the JAX-RS response
760         UriBuilder path = UriBuilder.fromResource(resourceClass);
761         path.path(parent.CSID + "/items/" + itemcsid);
762         result = Response.created(path.build()).build();
763
764         return result;
765     }
766
767     public PoxPayloadOut updateAuthorityItem(
768             ServiceContext<PoxPayloadIn, PoxPayloadOut> itemServiceCtx, // Ok to be null.  Will be null on PUT calls, but not on sync calls
769             ResourceMap resourceMap,
770             UriInfo uriInfo,
771             String parentspecifier,
772             String itemspecifier,
773             PoxPayloadIn theUpdate,
774             boolean shouldUpdateRevNumber,
775             Boolean isProposed,
776             Boolean isSASItem
777             ) throws Exception {
778         return updateAuthorityItem(null, itemServiceCtx, resourceMap, uriInfo, parentspecifier, itemspecifier, theUpdate, shouldUpdateRevNumber, isProposed, isSASItem);
779     }
780     
781     public PoxPayloadOut updateAuthorityItem(
782             ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx,
783             ServiceContext<PoxPayloadIn, PoxPayloadOut> itemServiceCtx, // Ok to be null.  Will be null on PUT calls, but not on sync calls
784             ResourceMap resourceMap,
785             UriInfo uriInfo,
786             String parentspecifier,
787             String itemspecifier,
788             PoxPayloadIn theUpdate,
789             boolean shouldUpdateRevNumber,
790             Boolean isProposed,
791             Boolean isSASItem
792             ) throws Exception {
793         PoxPayloadOut result = null;
794
795         CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
796         String parentcsid = csidAndShortId.CSID;
797         String parentShortId = csidAndShortId.shortIdentifier;
798         //
799         // If the itemServiceCtx context is not null, use it.  Otherwise, create a new context
800         //
801         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
802         if (ctx == null) {
803             ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
804             if (parentCtx != null && parentCtx.getCurrentRepositorySession() != null) {
805                 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
806             }
807         } else {
808             ctx.setInput(theUpdate); // the update payload
809         }
810
811         String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
812
813         // We omit the parentShortId, only needed when doing a create...
814         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
815         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
816         //
817         // Update the SAS fields if either value is non-null
818         //
819         boolean updateSASFields = isProposed != null || isSASItem != null;
820         handler.setshouldUpdateSASFields(updateSASFields);
821         if (updateSASFields == true) {
822             handler.setshouldUpdateSASFields(true);
823             if (isProposed != null) {
824                 handler.setIsProposed(isProposed);
825             }
826             if (isSASItem != null) {
827                 handler.setIsSASItem(isSASItem);
828             }
829         }
830
831         getRepositoryClient(ctx).update(ctx, itemcsid, handler);
832         result = ctx.getOutput();
833
834         return result;
835     }
836
837     /**
838      * Called with an existing context.
839      * @param parentCtx
840      * @param parentIdentifier
841      * @param input
842      * @return
843      * @throws Exception
844      */
845     public Response createAuthorityItemWithParentContext(ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx,
846             String parentIdentifier,
847             PoxPayloadIn input,
848             boolean shouldUpdateRevNumber,
849             boolean isProposed,
850             boolean isSASItem) throws Exception {
851         Response result = null;
852
853         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
854                 parentCtx.getResourceMap(), parentCtx.getUriInfo());
855         if (parentCtx.getCurrentRepositorySession() != null) {
856             ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
857         }
858         result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
859
860         return result;
861     }
862
863     /*************************************************************************
864      * Create an AuthorityItem - this is a sub-resource of Authority
865      * @param specifier either a CSID or one of the urn forms
866      * @return Authority item response
867      *************************************************************************/
868     @POST
869     @Path("{csid}/items")
870     public Response createAuthorityItem(
871             @Context ResourceMap resourceMap,
872             @Context UriInfo uriInfo,
873             @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
874             String xmlPayload) {
875         uriInfo = new UriInfoWrapper(uriInfo);
876         Response result = null;
877
878         try {
879             PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
880             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
881             result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
882                     AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
883         } catch (Exception e) {
884             throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
885         }
886
887         return result;
888     }
889
890     @GET
891     @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
892     public byte[] getItemWorkflow(
893             @PathParam("csid") String csid,
894             @PathParam("itemcsid") String itemcsid) {
895         PoxPayloadOut result = null;
896
897         try {
898             ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
899             String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
900
901             MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
902             WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
903             ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
904             getRepositoryClient(ctx).get(ctx, itemcsid, handler);
905             result = ctx.getOutput();
906         } catch (Exception e) {
907             throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
908         }
909         return result.getBytes();
910     }
911
912     /*
913      * We should consider changing this code.  The RepositoryClient (from call to getRepositoryClient) could support a call doWorkflowTransition() instead?
914      */
915     @Override
916     @PUT
917     @Path("{csid}" + WorkflowClient.SERVICE_PATH + "/" + "{transition}")
918     public byte[] updateWorkflowWithTransition(
919             @Context UriInfo uriInfo,
920             @PathParam("csid") String specifier,
921             @PathParam("transition") String transition) {
922         PoxPayloadOut result = null;
923
924         Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
925         String csid = null;
926         try {
927             csid = getCsid(null, spec);
928             result = updateWorkflowWithTransition(NULL_CONTEXT, uriInfo, csid, transition);
929         } catch (Exception e) {
930             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
931         }
932
933         return result.getBytes();
934     }
935
936     @PUT
937     @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
938     public byte[] updateItemWorkflowWithTransition(
939             @Context UriInfo uriInfo,
940             @PathParam("csid") String parentIdentifier,
941             @PathParam("itemcsid") String itemIdentifier,
942             @PathParam("transition") String transition) {
943         uriInfo = new UriInfoWrapper(uriInfo);
944         return updateItemWorkflowWithTransition(null, uriInfo, parentIdentifier, itemIdentifier, transition);
945     }
946     
947     public byte[] updateItemWorkflowWithTransition(
948             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
949             UriInfo uriInfo,
950             String parentIdentifier,
951             String itemIdentifier,
952             String transition) {
953         uriInfo = new UriInfoWrapper(uriInfo);
954         PoxPayloadOut result = null;
955
956         try {
957             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
958             if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
959                 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
960             }
961             result = updateItemWorkflowWithTransition(ctx,
962                     parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
963         } catch (Exception e) {
964             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
965         }
966
967         return result.getBytes();
968     }
969
970     public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
971             String parentIdentifier,
972             String itemIdentifier,
973             String transition,
974             boolean updateRevNumber) throws DocumentReferenceException {
975         return updateItemWorkflowWithTransition(existingContext, parentIdentifier, itemIdentifier, transition, updateRevNumber, true);
976     }
977
978     /**
979      * Update an authority item's workflow state.
980      * @param existingContext
981      * @param csid
982      * @param itemcsid
983      * @param transition
984      * @return
985      * @throws DocumentReferenceException
986      */
987     public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
988             String parentIdentifier,
989             String itemIdentifier,
990             String transition,
991             boolean updateRevNumber,
992             boolean rollbackOnException) throws DocumentReferenceException {
993         PoxPayloadOut result = null;
994
995         try {
996             //
997             // We need CSIDs for both the parent authority and the authority item
998             //
999             CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
1000             String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
1001
1002             //
1003             // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
1004             //
1005             PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(),
1006                     WorkflowClient.SERVICE_COMMONPART_NAME);
1007             MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
1008             ctx.setRollbackOnException(rollbackOnException);
1009             if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1010                 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
1011             }
1012             //
1013             // Create a service context and document handler for the target resource -not the workflow resource itself.
1014             //
1015             ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
1016             AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
1017             targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
1018             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
1019             //
1020             // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
1021             //
1022             String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
1023             ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
1024
1025             // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
1026             TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
1027             if (transitionDef == null) {
1028                 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
1029                         itemIdentifier, transition));
1030             }
1031             ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
1032
1033             WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
1034             getRepositoryClient(ctx).update(ctx, itemCsid, handler);
1035             result = ctx.getOutput();
1036         } catch (DocumentReferenceException de) {
1037             throw de;
1038         } catch (Exception e) {
1039             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
1040         }
1041
1042         return result;
1043     }
1044
1045     protected PoxPayloadOut getAuthorityItem(
1046             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1047             String parentIdentifier,
1048             String itemIdentifier) throws Exception {
1049         PoxPayloadOut result = null;
1050
1051         String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
1052         // We omit the parentShortId, only needed when doing a create...
1053         DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
1054
1055         Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
1056         if (itemSpec.form == SpecifierForm.CSID) {
1057             // TODO should we assert that the item is in the passed vocab?
1058             getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
1059         } else {
1060             String itemWhereClause =
1061                     RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
1062             DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
1063             handler.setDocumentFilter(myFilter);
1064             getRepositoryClient(ctx).get(ctx, handler);
1065         }
1066
1067         result = (PoxPayloadOut) ctx.getOutput();
1068         if (result != null) {
1069             String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
1070             if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
1071                 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
1072                         itemSpec.value, inAuthority, parentcsid));
1073             }
1074         }
1075
1076         return result;
1077     }
1078
1079     public PoxPayloadOut getAuthorityItemWithExistingContext(
1080             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1081             String parentIdentifier,
1082             String itemIdentifier) throws Exception {
1083         return getAuthorityItemWithExistingContext(existingCtx, existingCtx.getUriInfo(), existingCtx.getResourceMap(), parentIdentifier, itemIdentifier);
1084     }
1085
1086     public PoxPayloadOut getAuthorityItemWithExistingContext(
1087             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1088             UriInfo uriInfo,
1089             ResourceMap resourceMap,
1090             String parentIdentifier,
1091             String itemIdentifier) throws Exception {
1092         PoxPayloadOut result = null;
1093
1094         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1095         if (existingCtx.getCurrentRepositorySession() != null) {
1096             ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
1097             ctx.setProperties(existingCtx.getProperties());
1098         }
1099         result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1100
1101         return result;
1102     }
1103
1104     /**
1105      * Gets the authority item.
1106      *
1107      * @param parentspecifier either a CSID or one of the urn forms
1108      * @param itemspecifier either a CSID or one of the urn forms
1109      *
1110      * @return the authority item
1111      */
1112     @GET
1113     @Path("{csid}/items/{itemcsid}")
1114     public byte[] getAuthorityItem(
1115             @Context Request request,
1116             @Context UriInfo uriInfo,
1117             @Context ResourceMap resourceMap,
1118             @PathParam("csid") String parentIdentifier,
1119             @PathParam("itemcsid") String itemIdentifier) {
1120         uriInfo = new UriInfoWrapper(uriInfo);
1121         PoxPayloadOut result = null;
1122
1123         result = this.getAuthorityItemPayload(request, uriInfo, resourceMap, parentIdentifier, itemIdentifier);
1124
1125         return result.getBytes();
1126     }
1127
1128
1129     public PoxPayloadOut getAuthorityItemPayload(
1130             @Context Request request,
1131             @Context UriInfo uriInfo,
1132             @Context ResourceMap resourceMap,
1133             @PathParam("csid") String parentIdentifier,
1134             @PathParam("itemcsid") String itemIdentifier) {
1135         uriInfo = new UriInfoWrapper(uriInfo);
1136         PoxPayloadOut result = null;
1137         try {
1138             RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx =
1139                     (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1140
1141             JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1142             ctx.setJaxRsContext(jaxRsContext);
1143
1144             result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1145         } catch (DocumentNotFoundException dnf) {
1146             throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1147         } catch (Exception e) {
1148             throw bigReThrow(e, ServiceMessages.GET_FAILED);
1149         }
1150
1151         return result;
1152     }
1153
1154     public Response getAuthorityItemResponse(
1155             @Context Request request,
1156             @Context UriInfo uriInfo,
1157             @Context ResourceMap resourceMap,
1158             @PathParam("csid") String parentIdentifier,
1159             @PathParam("itemcsid") String itemIdentifier) {
1160         uriInfo = new UriInfoWrapper(uriInfo);
1161         PoxPayloadOut payloadout = null;
1162         RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
1163
1164         try {
1165             ctx = (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1166
1167             JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1168             ctx.setJaxRsContext(jaxRsContext);
1169
1170             payloadout = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1171         } catch (DocumentNotFoundException dnf) {
1172             throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1173         } catch (Exception e) {
1174             throw bigReThrow(e, ServiceMessages.GET_FAILED);
1175         }
1176
1177         return buildResponse(ctx, payloadout);
1178     }
1179
1180
1181     /*
1182      * Most of the authority child classes will/should use this implementation.  However, the Vocabulary service's item schema is
1183      * different enough that it will have to override this method in it's resource class.
1184      */
1185     @Override
1186     protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1187         String result = null;
1188
1189         result = NuxeoUtils.getPrimaryElPathPropertyName(
1190                 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1191                 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1192
1193         return result;
1194     }
1195
1196     @Override
1197     protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1198         String result = null;
1199
1200         result = NuxeoUtils.getMultiElPathPropertyName(
1201                 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1202                 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1203
1204         return result;
1205     }
1206
1207     /**
1208      * Gets the authorityItem list for the specified authority
1209      * If partialPerm is specified, keywords will be ignored.
1210      *
1211      * @param authorityIdentifier either a CSID or one of the urn forms
1212      * @param partialTerm if non-null, matches partial terms
1213      * @param keywords if non-null, matches terms in the keyword index for items
1214      * @param ui passed to include additional parameters, like pagination controls
1215      *
1216      */
1217     public AbstractCommonList getAuthorityItemList(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1218             String authorityIdentifier,
1219             UriInfo uriInfo) throws Exception {
1220         AbstractCommonList result = null;
1221
1222         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1223         MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1224         if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
1225             ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
1226             ctx.setProperties(existingContext.getProperties());
1227         }
1228
1229         String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
1230         String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
1231         String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
1232         String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
1233         String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
1234
1235         // For the wildcard case, parentcsid is null, but docHandler will deal with this.
1236         // We omit the parentShortId, only needed when doing a create...
1237         String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
1238             lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
1239         DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
1240             createItemDocumentHandler(ctx, parentcsid, null);
1241
1242         DocumentFilter myFilter = handler.getDocumentFilter();
1243         // If we are not wildcarding the parent, add a restriction
1244         if (parentcsid != null) {
1245             myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
1246                     + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
1247                     + "'" + parentcsid + "'",
1248                     IQueryManager.SEARCH_QUALIFIER_AND);
1249         }
1250
1251         if (Tools.notBlank(termStatus)) {
1252             // Start with the qualified termStatus field
1253             String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
1254                     + AuthorityItemJAXBSchema.TERM_STATUS;
1255             String[] filterTerms = termStatus.trim().split("\\|");
1256             String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
1257             myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
1258         }
1259
1260         result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
1261
1262         return result;
1263     }
1264
1265     /**
1266      * Gets the authorityItem list for the specified authority
1267      * If partialPerm is specified, keywords will be ignored.
1268      *
1269      * @param authorityIdentifier either a CSID or one of the urn forms
1270      * @param partialTerm if non-null, matches partial terms
1271      * @param keywords if non-null, matches terms in the keyword index for items
1272      * @param ui passed to include additional parameters, like pagination controls
1273      *
1274      * @return the authorityItem list
1275      */
1276     @GET
1277     @Path("{csid}/items")
1278     @Produces("application/xml")
1279     public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
1280             @Context UriInfo uriInfo) {
1281         uriInfo = new UriInfoWrapper(uriInfo);
1282         AbstractCommonList result = null;
1283
1284         try {
1285             result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);
1286         } catch (Exception e) {
1287             throw bigReThrow(e, ServiceMessages.LIST_FAILED);
1288         }
1289
1290         return result;
1291     }
1292
1293     /**
1294      * @return the name of the property used to specify references for items in this type of
1295      * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
1296      * Some types (like Vocabulary) use a separate property.
1297      */
1298     protected String getRefPropName() {
1299         return ServiceBindingUtils.AUTH_REF_PROP;
1300     }
1301
1302     /**
1303      * Gets the entities referencing this Authority item instance. The service type
1304      * can be passed as a query param "type", and must match a configured type
1305      * for the service bindings. If not set, the type defaults to
1306      * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1307      *
1308      * @param parentspecifier either a CSID or one of the urn forms
1309      * @param itemspecifier either a CSID or one of the urn forms
1310      * @param ui the ui
1311      *
1312      * @return the info for the referencing objects
1313      */
1314     @GET
1315     @Path("{csid}/items/{itemcsid}/refObjs")
1316     @Produces("application/xml")
1317     public AuthorityRefDocList getReferencingObjects(
1318             @PathParam("csid") String parentSpecifier,
1319             @PathParam("itemcsid") String itemSpecifier,
1320             @Context UriInfo uriInfo) {
1321         uriInfo = new UriInfoWrapper(uriInfo);
1322         AuthorityRefDocList authRefDocList = null;
1323         try {
1324             authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriInfo, PAGE_NUM_FROM_QUERYPARAMS, PAGE_SIZE_FROM_QUERYPARAMS, true, true);
1325         } catch (Exception e) {
1326             throw bigReThrow(e, ServiceMessages.GET_FAILED);
1327         }
1328
1329         if (authRefDocList == null) {
1330             Response response = Response.status(Response.Status.NOT_FOUND).entity(
1331                     "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1332                     "text/plain").build();
1333             throw new CSWebApplicationException(response);
1334         }
1335         return authRefDocList;
1336     }
1337
1338     public AuthorityRefDocList getReferencingObjects(
1339             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1340             String parentspecifier,
1341             String itemspecifier,
1342             UriInfo uriInfo,
1343             Integer pageNum,
1344             Integer pageSize,
1345             boolean useDefaultOrderByClause,
1346             boolean computeTotal) throws Exception {
1347         AuthorityRefDocList authRefDocList = null;
1348
1349         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1350         MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1351         //
1352         // Merge parts of existing context with our new context
1353         //
1354         if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1355             ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());  // If one exists, use the existing repo session
1356             ctx.setProperties(existingContext.getProperties());
1357         }
1358
1359         String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1360         String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1361
1362         // Remove the "type" property from the query params
1363         List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1364         if (serviceTypes == null || serviceTypes.isEmpty()) {
1365             serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1366         }
1367
1368         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1369         authRefDocList = handler.getReferencingObjects(ctx, serviceTypes, getRefPropName(), itemcsid, pageNum, pageSize, useDefaultOrderByClause, computeTotal);
1370
1371         return authRefDocList;
1372     }
1373
1374     /**
1375      * Gets the authority terms used in the indicated Authority item.
1376      *
1377      * @param parentspecifier either a CSID or one of the urn forms
1378      * @param itemspecifier either a CSID or one of the urn forms
1379      * @param ui passed to include additional parameters, like pagination controls
1380      *
1381      * @return the authority refs for the Authority item.
1382      */
1383     @GET
1384     @Path("{csid}/items/{itemcsid}/authorityrefs")
1385     @Produces("application/xml")
1386     public AuthorityRefList getAuthorityItemAuthorityRefs(
1387             @PathParam("csid") String parentspecifier,
1388             @PathParam("itemcsid") String itemspecifier,
1389             @Context UriInfo uriInfo) {
1390         uriInfo = new UriInfoWrapper(uriInfo);
1391         AuthorityRefList authRefList = null;
1392
1393         try {
1394             // Note that we have to create the service context for the Items, not the main service
1395             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1396             String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1397             // We omit the parentShortId, only needed when doing a create...
1398             DocumentModelHandler<?, AbstractCommonList> handler =
1399                     (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1400
1401             String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1402
1403             List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1404             authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1405         } catch (Exception e) {
1406             throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1407         }
1408
1409         return authRefList;
1410     }
1411
1412     /**
1413      * Synchronizes a local authority item with a share authority server (SAS) item.
1414      * @param ctx
1415      * @param parentIdentifier
1416      * @param itemIdentifier
1417      * @return
1418      * @throws Exception
1419      */
1420     private PoxPayloadOut synchronizeItem(
1421             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1422             String parentIdentifier,
1423             String itemIdentifier,
1424             boolean syncHierarchicalRelationships) throws Exception {
1425         PoxPayloadOut result = null;
1426         AuthorityItemSpecifier specifier;
1427         boolean neededSync = false;
1428
1429         CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1430         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1431         handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1432         handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1433         handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1434         // Create an authority item specifier
1435         Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1436         Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1437         specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1438         //
1439         neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1440         if (neededSync == true) {
1441             result = (PoxPayloadOut) ctx.getOutput();
1442         }
1443
1444         return result;
1445     }
1446
1447     /**
1448      * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1449      * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1450      * local authority.  The parent context was created for the authority (parent) because the sync started there.
1451      * @param existingCtx
1452      * @param parentIdentifier
1453      * @param itemIdentifier
1454      * @return
1455      * @throws Exception
1456      */
1457     public PoxPayloadOut synchronizeItemWithExistingContext(
1458             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1459             String parentIdentifier,
1460             String itemIdentifier,
1461             boolean syncHierarchicalRelationships
1462             ) throws Exception {
1463         PoxPayloadOut result = null;
1464
1465         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1466                 existingCtx.getResourceMap(),
1467                 existingCtx.getUriInfo());
1468         if (existingCtx.getCurrentRepositorySession() != null) {
1469             ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1470
1471         }
1472         result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1473
1474         return result;
1475     }
1476
1477     /**
1478      * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1479      *
1480      * @param specifier either CSIDs and/or one of the urn forms
1481      *
1482      * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1483      */
1484     @POST
1485     @Path("{csid}/items/{itemcsid}/sync")
1486     public byte[] synchronizeItem(
1487             @Context ResourceMap resourceMap,
1488             @Context UriInfo uriInfo,
1489             @PathParam("csid") String parentIdentifier,
1490             @PathParam("itemcsid") String itemIdentifier) {
1491         uriInfo = new UriInfoWrapper(uriInfo);
1492         byte[] result;
1493         boolean neededSync = false;
1494         PoxPayloadOut payloadOut = null;
1495
1496         try {
1497             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1498             payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1499             if (payloadOut != null) {
1500                 neededSync = true;
1501             }
1502         } catch (Exception e) {
1503             throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1504         }
1505
1506         //
1507         // If a sync was needed and was successful, return a copy of the updated resource.  Acts like an UPDATE.
1508         //
1509         if (neededSync == true) {
1510             result = payloadOut.getBytes();
1511         } else {
1512             result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1513                     itemIdentifier).getBytes();
1514             Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1515             throw new CSWebApplicationException(response);
1516         }
1517
1518         return result;
1519     }
1520
1521     /**
1522      * Update authorityItem.
1523      *
1524      * @param parentspecifier either a CSID or one of the urn forms
1525      * @param itemspecifier either a CSID or one of the urn forms
1526      *
1527      * @return the multipart output
1528      */
1529     @PUT
1530     @Path("{csid}/items/{itemcsid}")
1531     public byte[] updateAuthorityItem(
1532             @Context ResourceMap resourceMap,
1533             @Context UriInfo uriInfo,
1534             @PathParam("csid") String parentSpecifier,
1535             @PathParam("itemcsid") String itemSpecifier,
1536             String xmlPayload) {
1537         return updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, xmlPayload);
1538     }
1539  
1540     public byte[] updateAuthorityItem(
1541             ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx,
1542             ResourceMap resourceMap,
1543             UriInfo uriInfo,
1544             String parentSpecifier,
1545             String itemSpecifier,
1546             String xmlPayload) {
1547         uriInfo = new UriInfoWrapper(uriInfo);
1548         PoxPayloadOut result = null;
1549
1550         try {
1551             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1552             result = updateAuthorityItem(parentCtx, null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1553                     AuthorityServiceUtils.UPDATE_REV,            // passing TRUE so rev num increases, passing
1554                     AuthorityServiceUtils.NO_CHANGE,    // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1555                     AuthorityServiceUtils.NO_CHANGE);    // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1556         } catch (Exception e) {
1557             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1558         }
1559
1560         return result.getBytes();
1561     }
1562
1563     /**
1564      * Delete authorityItem.
1565      *
1566      * @param parentIdentifier the parentcsid
1567      * @param itemIdentifier the itemcsid
1568      *
1569      * @return the response
1570      */
1571     @DELETE
1572     @Path("{csid}/items/{itemcsid}")
1573     public Response deleteAuthorityItem(
1574             @Context UriInfo uriInfo,
1575             @PathParam("csid") String parentIdentifier,
1576             @PathParam("itemcsid") String itemIdentifier) {
1577         uriInfo = new UriInfoWrapper(uriInfo);
1578         Response result = null;
1579
1580         ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1581         ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1582         if (logger.isDebugEnabled()) {
1583             logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1584         }
1585
1586         try {
1587             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1588             deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1589             result = Response.status(HttpResponseCodes.SC_OK).build();
1590         } catch (Exception e) {
1591             throw bigReThrow(e, ServiceMessages.DELETE_FAILED + "  itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1592         }
1593
1594         return result;
1595     }
1596
1597     public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1598             String parentIdentifier,
1599             String itemIdentifier,
1600             boolean shouldUpdateRevNumber) throws Exception {
1601         return deleteAuthorityItem(existingCtx, parentIdentifier, itemIdentifier, shouldUpdateRevNumber, true);
1602     }
1603
1604     /**
1605      *
1606      * @param existingCtx
1607      * @param parentIdentifier
1608      * @param itemIdentifier
1609      * @throws Exception
1610      */
1611     public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1612             String parentIdentifier,
1613             String itemIdentifier,
1614             boolean shouldUpdateRevNumber,
1615             boolean rollbackOnException
1616             ) throws Exception {
1617         boolean result = true;
1618
1619         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1620         ctx.setRollbackOnException(rollbackOnException);
1621         if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1622             ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1623             ctx.setProperties(existingCtx.getProperties());
1624         }
1625
1626         String parentcsid = null;
1627         try {
1628             parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1629         } catch (DocumentNotFoundException de) {
1630             String msg = String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1631                     parentIdentifier, itemIdentifier);
1632             logger.warn(msg);
1633             throw de;
1634         }
1635         String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1636
1637         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1638         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1639         result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1640
1641         return result;
1642     }
1643
1644     @GET
1645     @Path("{csid}/items/{itemcsid}/" + hierarchy)
1646     @Produces("application/xml")
1647     public String getHierarchy(
1648             @PathParam("csid") String parentIdentifier,
1649             @PathParam("itemcsid") String itemIdentifier,
1650             @Context UriInfo uriInfo) throws Exception {
1651         uriInfo = new UriInfoWrapper(uriInfo);
1652         String result = null;
1653
1654         try {
1655             //
1656             // 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...?
1657             //
1658             String calledUri = uriInfo.getPath();
1659             String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1660             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1661
1662             String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1663             String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1664
1665             String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1666             if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1667                 result = Hierarchy.surface(ctx, itemcsid, uri);
1668             } else {
1669                 result = Hierarchy.dive(ctx, itemcsid, uri);
1670             }
1671         } catch (Exception e) {
1672             throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1673         }
1674
1675         return result;
1676     }
1677
1678     /**
1679      *
1680      * @param tenantId
1681      * @return
1682      */
1683     public String getItemDocType(String tenantId) {
1684         return getDocType(tenantId, getItemServiceName());
1685     }
1686
1687     /**
1688      * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1689      * for the current resource, for all tenants
1690      *
1691      * @return a map of URI templates for the current resource, for all tenants
1692      */
1693     @Override
1694     public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1695         Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1696                 super.getUriRegistryEntries();
1697         List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1698         for (String tenantId : tenantIds) {
1699                 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1700         }
1701         return uriRegistryEntriesMap;
1702     }
1703
1704     /**
1705      *
1706      */
1707     @Override
1708     public ServiceDescription getDescription(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1709         ServiceDescription result = super.getDescription(ctx);
1710         result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));
1711         return result;
1712     }
1713
1714     public Response createAuthority(String xmlPayload) {
1715         return this.createAuthority(null, null, xmlPayload);
1716     }
1717
1718     protected String getCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, Specifier specifier) throws Exception {
1719         String csid;
1720
1721         if (ctx == null) {
1722             ctx = createServiceContext(getServiceName());
1723         }
1724
1725         if (specifier.form == SpecifierForm.CSID) {
1726             csid = specifier.value;
1727         } else {
1728             String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, specifier.value);
1729             csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
1730         }
1731
1732         return csid;
1733     }
1734
1735 }