]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
d3c47014bad4e8ea3e53178bb2722c0a3b53338f
[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) throws Exception {
1343         return getReferencingObjects(existingContext, parentspecifier, itemspecifier, uriInfo, PAGE_NUM_FROM_QUERYPARAMS, PAGE_SIZE_FROM_QUERYPARAMS, true, true);
1344     }
1345
1346     public AuthorityRefDocList getReferencingObjects(
1347             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1348             String parentspecifier,
1349             String itemspecifier,
1350             UriInfo uriInfo,
1351             Integer pageNum,
1352             Integer pageSize,
1353             boolean useDefaultOrderByClause,
1354             boolean computeTotal) throws Exception {
1355         AuthorityRefDocList authRefDocList = null;
1356
1357         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1358         MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1359         //
1360         // Merge parts of existing context with our new context
1361         //
1362         if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1363             ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());  // If one exists, use the existing repo session
1364             ctx.setProperties(existingContext.getProperties());
1365         }
1366
1367         String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1368         String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1369
1370         // Remove the "type" property from the query params
1371         List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1372         if (serviceTypes == null || serviceTypes.isEmpty()) {
1373             serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1374         }
1375
1376         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1377         authRefDocList = handler.getReferencingObjects(ctx, serviceTypes, getRefPropName(), itemcsid, pageNum, pageSize, useDefaultOrderByClause, computeTotal);
1378
1379         return authRefDocList;
1380     }
1381
1382     /**
1383      * Gets the authority terms used in the indicated Authority item.
1384      *
1385      * @param parentspecifier either a CSID or one of the urn forms
1386      * @param itemspecifier either a CSID or one of the urn forms
1387      * @param ui passed to include additional parameters, like pagination controls
1388      *
1389      * @return the authority refs for the Authority item.
1390      */
1391     @GET
1392     @Path("{csid}/items/{itemcsid}/authorityrefs")
1393     @Produces("application/xml")
1394     public AuthorityRefList getAuthorityItemAuthorityRefs(
1395             @PathParam("csid") String parentspecifier,
1396             @PathParam("itemcsid") String itemspecifier,
1397             @Context UriInfo uriInfo) {
1398         uriInfo = new UriInfoWrapper(uriInfo);
1399         AuthorityRefList authRefList = null;
1400
1401         try {
1402             // Note that we have to create the service context for the Items, not the main service
1403             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1404             String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1405             // We omit the parentShortId, only needed when doing a create...
1406             DocumentModelHandler<?, AbstractCommonList> handler =
1407                     (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1408
1409             String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1410
1411             List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1412             authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1413         } catch (Exception e) {
1414             throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1415         }
1416
1417         return authRefList;
1418     }
1419
1420     /**
1421      * Synchronizes a local authority item with a share authority server (SAS) item.
1422      * @param ctx
1423      * @param parentIdentifier
1424      * @param itemIdentifier
1425      * @return
1426      * @throws Exception
1427      */
1428     private PoxPayloadOut synchronizeItem(
1429             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1430             String parentIdentifier,
1431             String itemIdentifier,
1432             boolean syncHierarchicalRelationships) throws Exception {
1433         PoxPayloadOut result = null;
1434         AuthorityItemSpecifier specifier;
1435         boolean neededSync = false;
1436
1437         CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1438         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1439         handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1440         handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1441         handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1442         // Create an authority item specifier
1443         Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1444         Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1445         specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1446         //
1447         neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1448         if (neededSync == true) {
1449             result = (PoxPayloadOut) ctx.getOutput();
1450         }
1451
1452         return result;
1453     }
1454
1455     /**
1456      * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1457      * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1458      * local authority.  The parent context was created for the authority (parent) because the sync started there.
1459      * @param existingCtx
1460      * @param parentIdentifier
1461      * @param itemIdentifier
1462      * @return
1463      * @throws Exception
1464      */
1465     public PoxPayloadOut synchronizeItemWithExistingContext(
1466             ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1467             String parentIdentifier,
1468             String itemIdentifier,
1469             boolean syncHierarchicalRelationships
1470             ) throws Exception {
1471         PoxPayloadOut result = null;
1472
1473         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1474                 existingCtx.getResourceMap(),
1475                 existingCtx.getUriInfo());
1476         if (existingCtx.getCurrentRepositorySession() != null) {
1477             ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1478
1479         }
1480         result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1481
1482         return result;
1483     }
1484
1485     /**
1486      * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1487      *
1488      * @param specifier either CSIDs and/or one of the urn forms
1489      *
1490      * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1491      */
1492     @POST
1493     @Path("{csid}/items/{itemcsid}/sync")
1494     public byte[] synchronizeItem(
1495             @Context ResourceMap resourceMap,
1496             @Context UriInfo uriInfo,
1497             @PathParam("csid") String parentIdentifier,
1498             @PathParam("itemcsid") String itemIdentifier) {
1499         uriInfo = new UriInfoWrapper(uriInfo);
1500         byte[] result;
1501         boolean neededSync = false;
1502         PoxPayloadOut payloadOut = null;
1503
1504         try {
1505             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1506             payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1507             if (payloadOut != null) {
1508                 neededSync = true;
1509             }
1510         } catch (Exception e) {
1511             throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1512         }
1513
1514         //
1515         // If a sync was needed and was successful, return a copy of the updated resource.  Acts like an UPDATE.
1516         //
1517         if (neededSync == true) {
1518             result = payloadOut.getBytes();
1519         } else {
1520             result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1521                     itemIdentifier).getBytes();
1522             Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1523             throw new CSWebApplicationException(response);
1524         }
1525
1526         return result;
1527     }
1528
1529     /**
1530      * Update authorityItem.
1531      *
1532      * @param parentspecifier either a CSID or one of the urn forms
1533      * @param itemspecifier either a CSID or one of the urn forms
1534      *
1535      * @return the multipart output
1536      */
1537     @PUT
1538     @Path("{csid}/items/{itemcsid}")
1539     public byte[] updateAuthorityItem(
1540             @Context ResourceMap resourceMap,
1541             @Context UriInfo uriInfo,
1542             @PathParam("csid") String parentSpecifier,
1543             @PathParam("itemcsid") String itemSpecifier,
1544             String xmlPayload) {
1545         return updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, xmlPayload);
1546     }
1547  
1548     public byte[] updateAuthorityItem(
1549             ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx,
1550             ResourceMap resourceMap,
1551             UriInfo uriInfo,
1552             String parentSpecifier,
1553             String itemSpecifier,
1554             String xmlPayload) {
1555         uriInfo = new UriInfoWrapper(uriInfo);
1556         PoxPayloadOut result = null;
1557
1558         try {
1559             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1560             result = updateAuthorityItem(parentCtx, null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1561                     AuthorityServiceUtils.UPDATE_REV,            // passing TRUE so rev num increases, passing
1562                     AuthorityServiceUtils.NO_CHANGE,    // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1563                     AuthorityServiceUtils.NO_CHANGE);    // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1564         } catch (Exception e) {
1565             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1566         }
1567
1568         return result.getBytes();
1569     }
1570
1571     /**
1572      * Delete authorityItem.
1573      *
1574      * @param parentIdentifier the parentcsid
1575      * @param itemIdentifier the itemcsid
1576      *
1577      * @return the response
1578      */
1579     @DELETE
1580     @Path("{csid}/items/{itemcsid}")
1581     public Response deleteAuthorityItem(
1582             @Context UriInfo uriInfo,
1583             @PathParam("csid") String parentIdentifier,
1584             @PathParam("itemcsid") String itemIdentifier) {
1585         uriInfo = new UriInfoWrapper(uriInfo);
1586         Response result = null;
1587
1588         ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1589         ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1590         if (logger.isDebugEnabled()) {
1591             logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1592         }
1593
1594         try {
1595             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1596             deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1597             result = Response.status(HttpResponseCodes.SC_OK).build();
1598         } catch (Exception e) {
1599             throw bigReThrow(e, ServiceMessages.DELETE_FAILED + "  itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1600         }
1601
1602         return result;
1603     }
1604
1605     public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1606             String parentIdentifier,
1607             String itemIdentifier,
1608             boolean shouldUpdateRevNumber) throws Exception {
1609         return deleteAuthorityItem(existingCtx, parentIdentifier, itemIdentifier, shouldUpdateRevNumber, true);
1610     }
1611
1612     /**
1613      *
1614      * @param existingCtx
1615      * @param parentIdentifier
1616      * @param itemIdentifier
1617      * @throws Exception
1618      */
1619     public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1620             String parentIdentifier,
1621             String itemIdentifier,
1622             boolean shouldUpdateRevNumber,
1623             boolean rollbackOnException
1624             ) throws Exception {
1625         boolean result = true;
1626
1627         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1628         ctx.setRollbackOnException(rollbackOnException);
1629         if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1630             ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1631             ctx.setProperties(existingCtx.getProperties());
1632         }
1633
1634         String parentcsid = null;
1635         try {
1636             parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1637         } catch (DocumentNotFoundException de) {
1638             String msg = String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1639                     parentIdentifier, itemIdentifier);
1640             logger.warn(msg);
1641             throw de;
1642         }
1643         String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1644
1645         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1646         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1647         result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1648
1649         return result;
1650     }
1651
1652     @GET
1653     @Path("{csid}/items/{itemcsid}/" + hierarchy)
1654     @Produces("application/xml")
1655     public String getHierarchy(
1656             @PathParam("csid") String parentIdentifier,
1657             @PathParam("itemcsid") String itemIdentifier,
1658             @Context UriInfo uriInfo) throws Exception {
1659         uriInfo = new UriInfoWrapper(uriInfo);
1660         String result = null;
1661
1662         try {
1663             //
1664             // 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...?
1665             //
1666             String calledUri = uriInfo.getPath();
1667             String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1668             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1669
1670             String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1671             String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1672
1673             String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1674             if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1675                 result = Hierarchy.surface(ctx, itemcsid, uri);
1676             } else {
1677                 result = Hierarchy.dive(ctx, itemcsid, uri);
1678             }
1679         } catch (Exception e) {
1680             throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1681         }
1682
1683         return result;
1684     }
1685
1686     /**
1687      *
1688      * @param tenantId
1689      * @return
1690      */
1691     public String getItemDocType(String tenantId) {
1692         return getDocType(tenantId, getItemServiceName());
1693     }
1694
1695     /**
1696      * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1697      * for the current resource, for all tenants
1698      *
1699      * @return a map of URI templates for the current resource, for all tenants
1700      */
1701     @Override
1702     public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1703         Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1704                 super.getUriRegistryEntries();
1705         List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1706         for (String tenantId : tenantIds) {
1707                 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1708         }
1709         return uriRegistryEntriesMap;
1710     }
1711
1712     /**
1713      *
1714      */
1715     @Override
1716     public ServiceDescription getDescription(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1717         ServiceDescription result = super.getDescription(ctx);
1718         result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));
1719         return result;
1720     }
1721
1722     public Response createAuthority(String xmlPayload) {
1723         return this.createAuthority(null, null, xmlPayload);
1724     }
1725
1726     protected String getCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, Specifier specifier) throws Exception {
1727         String csid;
1728
1729         if (ctx == null) {
1730             ctx = createServiceContext(getServiceName());
1731         }
1732
1733         if (specifier.form == SpecifierForm.CSID) {
1734             csid = specifier.value;
1735         } else {
1736             String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, specifier.value);
1737             csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
1738         }
1739
1740         return csid;
1741     }
1742
1743 }