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