]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
33c71980682aef9b4b994b2a7fd0f3b802a925b7
[tmp/jakarta-migration.git] /
1 /**
2  *  This document is a part of the source code and related artifacts
3  *  for CollectionSpace, an open source collections management system
4  *  for museums and related institutions:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
18  *  Unless required by applicable law or agreed to in writing, software
19  *  distributed under the License is distributed on an "AS IS" BASIS,
20  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  *  See the License for the specific language governing permissions and
22  *  limitations under the License.
23  */
24 package org.collectionspace.services.common.vocabulary;
25
26 import java.util.List;
27 import java.util.Map;
28
29 import javax.ws.rs.Consumes;
30 import javax.ws.rs.DELETE;
31 import javax.ws.rs.GET;
32 import javax.ws.rs.POST;
33 import javax.ws.rs.PUT;
34 import javax.ws.rs.Path;
35 import javax.ws.rs.PathParam;
36 import javax.ws.rs.Produces;
37 import javax.ws.rs.core.Context;
38 import javax.ws.rs.core.MultivaluedMap;
39 import javax.ws.rs.core.Request;
40 import javax.ws.rs.core.Response;
41 import javax.ws.rs.core.UriBuilder;
42 import javax.ws.rs.core.UriInfo;
43
44 import org.collectionspace.services.client.IClientQueryParams;
45 import org.collectionspace.services.client.IQueryManager;
46 import org.collectionspace.services.client.PoxPayloadIn;
47 import org.collectionspace.services.client.PoxPayloadOut;
48 import org.collectionspace.services.client.workflow.WorkflowClient;
49 import org.collectionspace.services.common.XmlTools;
50 import org.collectionspace.services.common.CSWebApplicationException;
51 import org.collectionspace.services.common.NuxeoBasedResource;
52 import org.collectionspace.services.common.ResourceMap;
53 import org.collectionspace.services.common.ServiceMain;
54 import org.collectionspace.services.common.ServiceMessages;
55 import org.collectionspace.services.common.StoredValuesUriTemplate;
56 import org.collectionspace.services.common.UriTemplateFactory;
57 import org.collectionspace.services.common.UriTemplateRegistry;
58 import org.collectionspace.services.common.UriTemplateRegistryKey;
59 import org.collectionspace.services.common.api.RefName;
60 import org.collectionspace.services.common.api.Tools;
61 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
62 import org.collectionspace.services.common.authorityref.AuthorityRefList;
63 import org.collectionspace.services.common.context.JaxRsContext;
64 import org.collectionspace.services.common.context.MultipartServiceContext;
65 import org.collectionspace.services.common.context.RemoteServiceContext;
66 import org.collectionspace.services.common.context.ServiceBindingUtils;
67 import org.collectionspace.services.common.context.ServiceContext;
68 import org.collectionspace.services.common.document.DocumentException;
69 import org.collectionspace.services.common.document.DocumentFilter;
70 import org.collectionspace.services.common.document.DocumentHandler;
71 import org.collectionspace.services.common.document.DocumentNotFoundException;
72 import org.collectionspace.services.common.document.DocumentReferenceException;
73 import org.collectionspace.services.common.document.DocumentWrapper;
74 import org.collectionspace.services.common.document.Hierarchy;
75 import org.collectionspace.services.common.query.QueryManager;
76 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
77 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
78 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
79 import org.collectionspace.services.config.ClientType;
80 import org.collectionspace.services.jaxb.AbstractCommonList;
81 import org.collectionspace.services.lifecycle.TransitionDef;
82 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
83 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
84 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentFilter;
85 import org.collectionspace.services.nuxeo.client.java.RepositoryClientImpl;
86 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
87 import org.collectionspace.services.workflow.WorkflowCommon;
88 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
89 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
90 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
91 import org.jboss.resteasy.util.HttpResponseCodes;
92 import org.nuxeo.ecm.core.api.DocumentModel;
93 import org.nuxeo.ecm.core.api.DocumentModelList;
94 import org.slf4j.Logger;
95 import org.slf4j.LoggerFactory;
96
97 /**
98  * The Class AuthorityResource.
99  */
100
101 @Consumes("application/xml")
102 @Produces("application/xml")
103 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
104         extends NuxeoBasedResource {
105         
106         final static String SEARCH_TYPE_TERMSTATUS = "ts";
107     public final static String hierarchy = "hierarchy";
108
109     protected Class<AuthCommon> authCommonClass;
110     protected Class<?> resourceClass;
111     protected String authorityCommonSchemaName;
112     protected String authorityItemCommonSchemaName;
113     final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed?  I see no references to it.
114         
115     final static String FETCH_SHORT_ID = "_fetch_";
116     public final static String PARENT_WILDCARD = "_ALL_";
117         
118     final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
119
120     /**
121      * Instantiates a new Authority resource.
122      */
123     public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
124             String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
125         this.authCommonClass = authCommonClass;
126         this.resourceClass = resourceClass;
127         this.authorityCommonSchemaName = authorityCommonSchemaName;
128         this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
129     }
130
131     public abstract String getItemServiceName();
132     
133     public abstract String getItemTermInfoGroupXPathBase();
134
135     @Override
136     protected String getVersionString() {
137         return "$LastChangedRevision: 2617 $";
138     }
139
140     @Override
141     public Class<AuthCommon> getCommonPartClass() {
142         return authCommonClass;
143     }
144
145     /**
146      * Creates the item document handler.
147      * 
148      * @param ctx the ctx
149      * @param inAuthority the in vocabulary
150      * 
151      * @return the document handler
152      * 
153      * @throws Exception the exception
154      */
155     protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
156             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
157             String inAuthority, String parentShortIdentifier)
158             throws Exception {
159         String authorityRefNameBase;
160         AuthorityItemDocumentModelHandler<?> docHandler;
161
162         if (parentShortIdentifier == null) {
163             authorityRefNameBase = null;
164         } else {
165             ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getServiceName());
166             if (parentShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
167                 if (ctx.getCurrentRepositorySession() != null) {
168                         parentCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
169                 }
170                 // Get from parent document
171                 parentShortIdentifier = getAuthShortIdentifier(parentCtx, inAuthority);
172             }
173             authorityRefNameBase = buildAuthorityRefNameBase(parentCtx, parentShortIdentifier);
174         }
175
176         docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
177                 ctx.getCommonPartLabel(getItemServiceName()),
178                 authCommonClass);
179         // FIXME - Richard and Aron think the following three lines should
180         // be in the constructor for the AuthorityItemDocumentModelHandler
181         // because all three are required fields.
182         docHandler.setInAuthority(inAuthority);
183         docHandler.setAuthorityRefNameBase(authorityRefNameBase);
184         docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
185         return docHandler;
186     }
187
188     public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
189             throws DocumentNotFoundException, DocumentException {
190         String shortIdentifier = null;
191         
192         try {
193             AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
194             shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
195         } catch (Exception e) {
196             if (logger.isDebugEnabled()) {
197                 logger.debug("Caught exception ", e);
198             }
199             throw new DocumentException(e);
200         }
201         
202         return shortIdentifier;
203     }
204
205     protected String buildAuthorityRefNameBase(
206             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
207         RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
208                 ctx.getServiceName(), 
209                 null,   // Only use shortId form!!!
210                 shortIdentifier, null);
211         return authority.toString();
212     }
213
214     public static class CsidAndShortIdentifier {
215         String CSID;
216         String shortIdentifier;
217     }
218
219         protected String lookupParentCSID(String parentspecifier, String method,
220                         String op, UriInfo uriInfo) throws Exception {
221                 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(null,
222                                 parentspecifier, method, op, uriInfo);
223                 return tempResult.CSID;
224         }
225         
226         protected String lookupParentCSID(ServiceContext ctx, String parentspecifier, String method,
227                         String op, UriInfo uriInfo) throws Exception {
228                 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
229                                 parentspecifier, method, op, uriInfo);
230                 return tempResult.CSID;
231         }
232
233
234     private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
235                 ServiceContext existingCtx, // Ok to be null
236                 String parentIdentifier,
237                 String method,
238                 String op,
239                 UriInfo uriInfo)
240             throws Exception {
241         CsidAndShortIdentifier result = new CsidAndShortIdentifier();
242         Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
243         
244         String parentcsid;
245         String parentShortIdentifier;
246         if (parentSpec.form == SpecifierForm.CSID) {
247             parentShortIdentifier = null;
248             parentcsid = parentSpec.value;
249             // Uncomment when app layer is ready to integrate
250             // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
251             parentShortIdentifier = FETCH_SHORT_ID;
252         } else {
253             parentShortIdentifier = parentSpec.value;
254             String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
255             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
256             CoreSessionInterface repoSession = null;
257             if (existingCtx != null) {
258                 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession();  // We want to use the thread's current repo session
259             }
260             parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
261         }
262         
263         result.CSID = parentcsid;
264         result.shortIdentifier = parentShortIdentifier;
265         
266         return result;
267     }
268
269     public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemspecifier, String parentcsid, String method, String op)
270             throws Exception {
271         String itemcsid;
272         Specifier itemSpec = Specifier.getSpecifier(itemspecifier, method, op);
273         if (itemSpec.form == SpecifierForm.CSID) {
274             itemcsid = itemSpec.value;
275         } else {
276             String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
277                 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
278             CoreSessionInterface repoSession = null;
279             if (existingContext != null) {
280                 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession();  // We want to use the thread's current repo session
281             }
282             itemcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
283         }
284         return itemcsid;
285     }
286
287     /*
288      * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then 
289      * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
290      * Resource. They then call this method on that resource.
291      */
292     @Override
293         public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item) 
294                         throws Exception, DocumentNotFoundException {
295         if(item == null) {
296                 return null;
297         }
298         String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
299         // Ensure we have the right context.
300         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
301         
302         // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
303         RepositoryClientImpl client = (RepositoryClientImpl)getRepositoryClient(ctx);
304         String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
305
306         String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
307         ctx = createServiceContext(getItemServiceName());
308         DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
309         DocumentModel docModel = docWrapper.getWrappedObject();
310         return docModel;
311     }
312
313
314     @POST
315     public Response createAuthority(String xmlPayload) {
316         //
317         // 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
318         // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
319         // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
320         // the code that creates new authorities.  The authority document model handler will first check for authorities with the same short id before
321         // trying to create a new authority.
322         //
323         synchronized(AuthorityResource.class) {
324                 try {
325                     PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
326                     ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
327                     DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
328                     
329                     String csid = getRepositoryClient(ctx).create(ctx, handler);
330                     UriBuilder path = UriBuilder.fromResource(resourceClass);
331                     path.path("" + csid);
332                     Response response = Response.created(path.build()).build();
333                     return response;
334                 } catch (Exception e) {
335                     throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
336                 }
337         }
338     }
339
340
341     /**
342      * Synchronizes the authority and its terms with a Shared Authority Server.
343      * 
344      * @param specifier either a CSID or one of the urn forms
345      * 
346      * @return the authority
347      */
348     @GET
349     @Path("{csid}/sync")
350     public byte[] synchronize(
351             @Context Request request,
352             @Context UriInfo ui,
353             @PathParam("csid") String csid) {
354         byte[] result;
355         boolean neededSync = false;
356         PoxPayloadOut payloadOut = null;
357         Specifier specifier;
358         
359         try {
360             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
361             AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
362             specifier = Specifier.getSpecifier(csid, "getAuthority", "GET");
363             handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
364             neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
365             payloadOut = ctx.getOutput();
366         } catch (Exception e) {
367             throw bigReThrow(e, ServiceMessages.SYNC_FAILED, csid);
368         }
369
370         //
371         // If a sync was needed and was successful, return a copy of the updated resource.  Acts like an UPDATE.
372         //
373         if (neededSync == true) {
374                 result = payloadOut.getBytes();
375         } else {
376                 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
377                                 specifier.value).getBytes();
378                 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
379             throw new CSWebApplicationException(response);
380         }
381                 return result;
382     }
383         
384     /**
385      * Gets the authority.
386      * 
387      * @param specifier either a CSID or one of the urn forms
388      * 
389      * @return the authority
390      */
391     @GET
392     @Path("{csid}")
393     @Override
394     public byte[] get(
395             @Context Request request,
396             @Context UriInfo ui,
397             @PathParam("csid") String specifier) {
398         PoxPayloadOut result = null;
399         try {
400             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
401             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
402
403             Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
404             if (spec.form == SpecifierForm.CSID) {
405                 if (logger.isDebugEnabled()) {
406                     logger.debug("getAuthority with csid=" + spec.value);
407                 }
408                 getRepositoryClient(ctx).get(ctx, spec.value, handler);
409             } else {
410                 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
411                 DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
412                 handler.setDocumentFilter(myFilter);
413                 getRepositoryClient(ctx).get(ctx, handler);
414             }
415             result = ctx.getOutput();
416
417         } catch (Exception e) {
418             throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
419         }
420
421         if (result == null) {
422             Response response = Response.status(Response.Status.NOT_FOUND).entity(
423                     "Get failed, the requested Authority specifier:" + specifier + ": was not found.").type(
424                     "text/plain").build();
425             throw new CSWebApplicationException(response);
426         }
427
428         return result.getBytes();
429     }
430
431     /**
432      * Finds and populates the authority list.
433      * 
434      * @param ui the ui
435      * 
436      * @return the authority list
437      */
438     @GET
439     @Produces("application/xml")
440     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.
441         AbstractCommonList result = null;
442         
443         try {
444             MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
445             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
446             
447             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
448             DocumentFilter myFilter = handler.getDocumentFilter();
449             // Need to make the default sort order for authority items
450             // be on the displayName field
451             String sortBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
452             if (sortBy == null || sortBy.isEmpty()) {
453                 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
454                         + AuthorityItemJAXBSchema.DISPLAY_NAME;
455                 myFilter.setOrderByClause(qualifiedDisplayNameField);
456             }
457             String nameQ = queryParams.getFirst("refName");
458             if (nameQ != null) {
459                 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
460             }
461             getRepositoryClient(ctx).getFiltered(ctx, handler);
462             result = handler.getCommonPartList();
463         } catch (Exception e) {
464             throw bigReThrow(e, ServiceMessages.GET_FAILED);
465         }
466         
467         return result;
468     }
469     
470     /**
471      * Overriding this methods to see if we should update the revision number during the update.  We don't
472      * want to update the rev number of synchronization operations.
473      */
474     @Override
475     protected PoxPayloadOut update(String csid,
476             PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
477             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
478             throws Exception {
479         AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
480         Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
481         if (shouldUpdateRev != null) {
482                 handler.setShouldUpdateRevNumber(shouldUpdateRev);
483         }
484         getRepositoryClient(ctx).update(ctx, csid, handler);
485         return ctx.getOutput();
486     }
487     
488     /**
489      * Update authority.
490      *
491      * @param specifier the csid or id
492      *
493      * @return the multipart output
494      */
495     @PUT
496     @Path("{csid}")
497     public byte[] updateAuthority(
498             @PathParam("csid") String specifier,
499             String xmlPayload) {
500         PoxPayloadOut result = null;
501         try {
502             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
503             Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
504             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
505             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
506             String csid;
507             if (spec.form == SpecifierForm.CSID) {
508                 csid = spec.value;
509             } else {
510                 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
511                 csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
512             }
513             getRepositoryClient(ctx).update(ctx, csid, handler);
514             result = ctx.getOutput();
515         } catch (Exception e) {
516             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
517         }
518         return result.getBytes();
519     }
520
521     /**
522      * Delete authority.
523      * 
524      * @param csid the csid
525      * 
526      * @return the response
527      */
528     @Deprecated
529 //    @DELETE
530     @Path("{csid}")
531     public Response old_deleteAuthority(@PathParam("csid") String csid) {
532         if (logger.isDebugEnabled()) {
533             logger.debug("deleteAuthority with csid=" + csid);
534         }
535         try {
536             ensureCSID(csid, ServiceMessages.DELETE_FAILED, "Authority.csid");
537             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
538             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
539             getRepositoryClient(ctx).delete(ctx, csid, handler);
540             return Response.status(HttpResponseCodes.SC_OK).build();
541         } catch (Exception e) {
542             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, csid);
543         }
544     }
545     
546     /**
547      * Delete authority
548      * 
549      * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
550      * 
551      * @return the response
552      */
553     @DELETE
554     @Path("{csid}")
555     public Response deleteAuthority(
556             @Context Request request,
557             @Context UriInfo ui,
558             @PathParam("csid") String specifier) {
559         if (logger.isDebugEnabled()) {
560             logger.debug("deleteAuthority with specifier=" + specifier);
561         }
562         
563         try {
564             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
565             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
566
567             Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
568             if (spec.form == SpecifierForm.CSID) {
569                 if (logger.isDebugEnabled()) {
570                     logger.debug("deleteAuthority with csid=" + spec.value);
571                 }
572                 ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
573                 getRepositoryClient(ctx).delete(ctx, spec.value, handler);
574             } else {
575                 if (logger.isDebugEnabled()) {
576                     logger.debug("deleteAuthority with specifier=" + spec.value);
577                 }               
578                 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
579                 getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
580             }
581             
582             return Response.status(HttpResponseCodes.SC_OK).build();
583         } catch (Exception e) {
584             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
585         }
586     }
587     
588     /**
589      * 
590      * @param ctx
591      * @param parentspecifier           - ID of the container. Can be URN or CSID form
592      * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
593      * @param proposed                          - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
594      * @return
595      * @throws Exception
596      */
597     protected Response createAuthorityItem(ServiceContext ctx, String parentIdentifier,
598                 boolean shouldUpdateRevNumber,
599                 boolean proposed) throws Exception {
600         Response result = null;
601         
602         // Note: must have the parentShortId, to do the create.
603         CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
604         AuthorityItemDocumentModelHandler handler = 
605                 (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
606         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
607         handler.setIsProposed(proposed);
608         // Make the client call
609         String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
610         
611         // Build the JAX-RS response
612         UriBuilder path = UriBuilder.fromResource(resourceClass);
613         path.path(parent.CSID + "/items/" + itemcsid);
614         result = Response.created(path.build()).build();
615
616         return result;
617     }
618
619     /**
620      * Called with an existing context.
621      * @param parentCtx
622      * @param parentIdentifier
623      * @param input
624      * @return
625      * @throws Exception
626      */
627     public Response createAuthorityItemWithParentContext(ServiceContext parentCtx,
628                 String parentIdentifier,
629                 PoxPayloadIn input,
630                 boolean shouldUpdateRevNumber,
631                 boolean isProposed) throws Exception {
632         Response result = null;
633         
634         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
635                         parentCtx.getResourceMap(), parentCtx.getUriInfo());
636         if (parentCtx.getCurrentRepositorySession() != null) {
637                 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
638         }
639         result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed);
640
641         return result;
642     }
643         
644     /*************************************************************************
645      * Create an AuthorityItem - this is a sub-resource of Authority
646      * @param specifier either a CSID or one of the urn forms
647      * @return Authority item response
648      *************************************************************************/
649     @POST
650     @Path("{csid}/items")
651     public Response createAuthorityItem(
652                 @Context ResourceMap resourceMap,
653                 @Context UriInfo uriInfo, 
654                 @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
655                 String xmlPayload) {
656         Response result = null;
657         
658         try {
659             PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
660             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
661             result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
662                         AuthorityServiceUtils.PROPOSED);
663         } catch (Exception e) {
664             throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
665         }
666         
667         return result;
668     }
669
670     @GET
671     @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
672     public byte[] getItemWorkflow(
673             @PathParam("csid") String csid,
674             @PathParam("itemcsid") String itemcsid) {
675         PoxPayloadOut result = null;
676
677         try {
678             ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
679             String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
680
681             MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
682             WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
683             ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
684             getRepositoryClient(ctx).get(ctx, itemcsid, handler);
685             result = ctx.getOutput();
686         } catch (Exception e) {
687             throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
688         }
689         return result.getBytes();
690     }
691
692     //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
693     // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
694     @PUT
695     @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
696     public byte[] updateItemWorkflowWithTransition(
697             @PathParam("csid") String parentIdentifier,
698             @PathParam("itemcsid") String itemIdentifier,
699             @PathParam("transition") String transition) {
700         PoxPayloadOut result = null;
701         
702         try {            
703             result = updateItemWorkflowWithTransition(NULL_CONTEXT, 
704                         parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
705         } catch (Exception e) {
706             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
707         }
708         
709         return result.getBytes();
710     }
711     
712     /**
713      * Update an authority item's workflow state.
714      * @param existingContext
715      * @param csid
716      * @param itemcsid
717      * @param transition
718      * @return
719      * @throws DocumentReferenceException 
720      */
721     public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext existingContext,
722             String parentIdentifier,
723             String itemIdentifier,
724             String transition,
725             boolean updateRevNumber) throws DocumentReferenceException {
726         PoxPayloadOut result = null;
727         
728         try {
729                 //
730                 // We need CSIDs for both the parent authority and the authority item
731                 //
732             CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
733             String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
734
735             //
736                 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
737                 //
738                 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(), 
739                                 WorkflowClient.SERVICE_COMMONPART_NAME);
740             MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
741             if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
742                 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If a repo session is already open, we need to use it and not create a new one
743             }
744             //
745             // Create a service context and document handler for the target resource -not the workflow resource itself.
746             //
747             ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName());
748             AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
749             targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
750             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
751             //
752             // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
753             //
754             String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
755             ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
756             
757                 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
758             TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
759             ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
760             
761             WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
762             getRepositoryClient(ctx).update(ctx, itemCsid, handler);
763             result = ctx.getOutput();
764         } catch (DocumentReferenceException de) {
765                 throw de;
766         } catch (Exception e) {
767             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
768         }
769         
770         return result;
771     }
772     
773     private PoxPayloadOut getAuthorityItem(
774                 ServiceContext ctx,
775             String parentIdentifier,
776             String itemIdentifier) throws Exception {
777         PoxPayloadOut result = null;
778         
779         String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
780         // We omit the parentShortId, only needed when doing a create...
781         DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
782
783         Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
784         if (itemSpec.form == SpecifierForm.CSID) {
785             // TODO should we assert that the item is in the passed vocab?
786             getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
787         } else {
788             String itemWhereClause =
789                         RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
790             DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
791             handler.setDocumentFilter(myFilter);
792             getRepositoryClient(ctx).get(ctx, handler);
793         }
794         
795         result = (PoxPayloadOut) ctx.getOutput();
796         if (result != null) {
797                 String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
798                 if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
799                         throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
800                                         itemSpec.value, inAuthority, parentcsid));
801                 }
802         }
803         
804         return result;
805     }
806
807     public PoxPayloadOut getAuthorityItemWithExistingContext(
808                 ServiceContext existingCtx,
809             String parentIdentifier,
810             String itemIdentifier) throws Exception {
811         PoxPayloadOut result = null;
812         
813         ServiceContext ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
814         if (existingCtx.getCurrentRepositorySession() != null) {
815                 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
816                 ctx.setProperties(existingCtx.getProperties());
817         }
818         result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
819         
820         return result;
821     }
822     
823     /**
824      * Gets the authority item.
825      * 
826      * @param parentspecifier either a CSID or one of the urn forms
827      * @param itemspecifier either a CSID or one of the urn forms
828      * 
829      * @return the authority item
830      */
831     @GET
832     @Path("{csid}/items/{itemcsid}")
833     public byte[] getAuthorityItem(
834             @Context Request request,
835             @Context UriInfo uriInfo,
836                 @Context ResourceMap resourceMap,            
837             @PathParam("csid") String parentIdentifier,
838             @PathParam("itemcsid") String itemIdentifier) {
839         PoxPayloadOut result = null;
840         try {
841             RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = 
842                         (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
843
844             JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
845             ctx.setJaxRsContext(jaxRsContext);
846             
847             result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
848         } catch (DocumentNotFoundException dnf) {
849             throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
850         } catch (Exception e) {
851             throw bigReThrow(e, ServiceMessages.GET_FAILED);
852         }
853                 
854         return result.getBytes();
855     }
856
857     /*
858      * Most of the authority child classes will/should use this implementation.  However, the Vocabulary service's item schema is
859      * different enough that it will have to override this method in it's resource class.
860      */
861     @Override
862         protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
863                 String result = null;
864
865                 result = NuxeoUtils.getPrimaryElPathPropertyName(
866                                 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
867                                 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
868
869                 return result;
870         }
871         
872     @Override
873         protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
874                 String result = null;
875                 
876                 result = NuxeoUtils.getMultiElPathPropertyName(
877                                 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
878                                 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
879
880                 return result;
881         }
882     
883     /**
884      * Gets the authorityItem list for the specified authority
885      * If partialPerm is specified, keywords will be ignored.
886      * 
887      * @param specifier either a CSID or one of the urn forms
888      * @param partialTerm if non-null, matches partial terms
889      * @param keywords if non-null, matches terms in the keyword index for items
890      * @param ui passed to include additional parameters, like pagination controls
891      *
892      */
893     public AbstractCommonList getAuthorityItemList(ServiceContext existingContext,
894                 String specifier,
895             UriInfo uriInfo) throws Exception {
896         AbstractCommonList result = null;
897         
898             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
899             MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
900         if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
901                 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
902                 ctx.setProperties(existingContext.getProperties());
903         }
904             
905             String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
906             String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
907             String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
908             String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
909             String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
910
911             // For the wildcard case, parentcsid is null, but docHandler will deal with this.
912             // We omit the parentShortId, only needed when doing a create...
913             String parentcsid = PARENT_WILDCARD.equals(specifier) ? null :
914                                 lookupParentCSID(specifier, "getAuthorityItemList", "LIST", uriInfo);
915             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
916                 createItemDocumentHandler(ctx, parentcsid, null);
917             
918             DocumentFilter myFilter = handler.getDocumentFilter();
919             // If we are not wildcarding the parent, add a restriction
920             if (parentcsid != null) {
921                     myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
922                             + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
923                             + "'" + parentcsid + "'",
924                             IQueryManager.SEARCH_QUALIFIER_AND);
925             }
926
927             if (Tools.notBlank(termStatus)) {
928                 // Start with the qualified termStatus field
929                 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
930                         + AuthorityItemJAXBSchema.TERM_STATUS;
931                 String[] filterTerms = termStatus.trim().split("\\|");
932                 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
933                 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
934             }
935
936             result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);            
937         
938         return result;
939     }
940     
941     /**
942      * Gets the authorityItem list for the specified authority
943      * If partialPerm is specified, keywords will be ignored.
944      * 
945      * @param specifier either a CSID or one of the urn forms
946      * @param partialTerm if non-null, matches partial terms
947      * @param keywords if non-null, matches terms in the keyword index for items
948      * @param ui passed to include additional parameters, like pagination controls
949      * 
950      * @return the authorityItem list
951      */
952     @GET
953     @Path("{csid}/items")
954     @Produces("application/xml")
955     public AbstractCommonList getAuthorityItemList(@PathParam("csid") String specifier,
956             @Context UriInfo uriInfo) {
957         AbstractCommonList result = null;
958         
959         try {
960             result = getAuthorityItemList(NULL_CONTEXT, specifier, uriInfo);    
961         } catch (Exception e) {
962             throw bigReThrow(e, ServiceMessages.LIST_FAILED);
963         }
964         
965         return result;
966     }
967
968     /**
969      * @return the name of the property used to specify references for items in this type of
970      * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
971      * Some types (like Vocabulary) use a separate property.
972      */
973     protected String getRefPropName() {
974         return ServiceBindingUtils.AUTH_REF_PROP;
975     }
976     
977     /**
978      * Gets the entities referencing this Authority item instance. The service type
979      * can be passed as a query param "type", and must match a configured type
980      * for the service bindings. If not set, the type defaults to
981      * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
982      *
983      * @param parentspecifier either a CSID or one of the urn forms
984      * @param itemspecifier either a CSID or one of the urn forms
985      * @param ui the ui
986      * 
987      * @return the info for the referencing objects
988      */
989     @GET
990     @Path("{csid}/items/{itemcsid}/refObjs")
991     @Produces("application/xml")
992     public AuthorityRefDocList getReferencingObjects(
993             @PathParam("csid") String parentSpecifier,
994             @PathParam("itemcsid") String itemSpecifier,
995             @Context UriTemplateRegistry uriTemplateRegistry,
996             @Context UriInfo uriInfo) {
997         AuthorityRefDocList authRefDocList = null;
998         try {
999             authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriTemplateRegistry, uriInfo);
1000         } catch (Exception e) {
1001             throw bigReThrow(e, ServiceMessages.GET_FAILED);
1002         }
1003         
1004         if (authRefDocList == null) {
1005             Response response = Response.status(Response.Status.NOT_FOUND).entity(
1006                     "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1007                     "text/plain").build();
1008             throw new CSWebApplicationException(response);
1009         }
1010         return authRefDocList;
1011     }
1012     
1013     public AuthorityRefDocList getReferencingObjects(
1014                 ServiceContext existingContext,
1015             String parentspecifier,
1016             String itemspecifier,
1017             UriTemplateRegistry uriTemplateRegistry,
1018             UriInfo uriInfo) throws Exception {
1019         AuthorityRefDocList authRefDocList = null;
1020  
1021         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1022         MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1023         //
1024         // Merge parts of existing context with our new context
1025         //
1026         if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1027                 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());  // If one exists, use the existing repo session
1028                 ctx.setProperties(existingContext.getProperties());
1029         }
1030
1031         String parentcsid = lookupParentCSID(parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1032         String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1033
1034         List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1035         if (serviceTypes == null || serviceTypes.isEmpty()) {
1036                 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1037         }
1038             
1039         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1040         authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
1041
1042         return authRefDocList;
1043     }
1044
1045     /**
1046      * Gets the authority terms used in the indicated Authority item.
1047      *
1048      * @param parentspecifier either a CSID or one of the urn forms
1049      * @param itemspecifier either a CSID or one of the urn forms
1050      * @param ui passed to include additional parameters, like pagination controls
1051      *
1052      * @return the authority refs for the Authority item.
1053      */
1054     @GET
1055     @Path("{csid}/items/{itemcsid}/authorityrefs")
1056     @Produces("application/xml")
1057     public AuthorityRefList getAuthorityItemAuthorityRefs(
1058             @PathParam("csid") String parentspecifier,
1059             @PathParam("itemcsid") String itemspecifier,
1060             @Context UriInfo uriInfo) {
1061         AuthorityRefList authRefList = null;
1062         try {
1063             // Note that we have to create the service context for the Items, not the main service
1064             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1065             MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1066             String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1067             // We omit the parentShortId, only needed when doing a create...
1068             DocumentModelHandler<?, AbstractCommonList> handler =
1069                     (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1070
1071             String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1072
1073             List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1074             authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1075         } catch (Exception e) {
1076             throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1077         }
1078         return authRefList;
1079     }
1080     
1081     /**
1082      * Synchronizes a local authority item with a share authority server (SAS) item.
1083      * @param ctx
1084      * @param parentIdentifier
1085      * @param itemIdentifier
1086      * @return
1087      * @throws Exception
1088      */
1089     private PoxPayloadOut synchronizeItem(
1090                 ServiceContext ctx,
1091             String parentIdentifier,
1092             String itemIdentifier) throws Exception {
1093         PoxPayloadOut result = null;
1094         AuthorityItemSpecifier specifier;
1095         boolean neededSync = false;
1096
1097         CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1098         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1099         handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1100         // Create an authority item specifier
1101         Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1102         Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1103         specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1104         //
1105         neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1106         if (neededSync == true) {
1107                 result = (PoxPayloadOut) ctx.getOutput();
1108         }
1109         
1110         return result;
1111     }
1112
1113     /**
1114      * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1115      * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1116      * local authority.  The parent context was created for the authority (parent) because the sync started there.
1117      * @param existingCtx
1118      * @param parentIdentifier
1119      * @param itemIdentifier
1120      * @return
1121      * @throws Exception
1122      */
1123     public PoxPayloadOut synchronizeItemWithExistingContext(
1124                 ServiceContext existingCtx,
1125             String parentIdentifier,
1126             String itemIdentifier
1127             ) throws Exception {
1128         PoxPayloadOut result = null;
1129         
1130         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1131                         existingCtx.getResourceMap(),
1132                         existingCtx.getUriInfo());
1133         if (existingCtx.getCurrentRepositorySession() != null) {
1134                 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1135         }
1136         result = synchronizeItem(ctx, parentIdentifier, itemIdentifier);
1137         
1138         return result;
1139     }
1140     
1141     /**
1142      * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1143      * 
1144      * @param specifier either CSIDs and/or one of the urn forms
1145      * 
1146      * @return the authority item if it was synchronized with SAS
1147      */
1148     @GET
1149     @Path("{csid}/items/{itemcsid}/sync")
1150     public byte[] synchronizeItem(
1151                 @Context ResourceMap resourceMap,
1152             @Context UriInfo uriInfo,
1153             @PathParam("csid") String parentIdentifier,
1154             @PathParam("itemcsid") String itemIdentifier) {
1155         byte[] result;
1156         boolean neededSync = false;
1157         PoxPayloadOut payloadOut = null;
1158         
1159         try {
1160             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1161             payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier);
1162             if (payloadOut != null) {
1163                 neededSync = true;
1164             }
1165         } catch (Exception e) {
1166             throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1167         }
1168
1169         //
1170         // If a sync was needed and was successful, return a copy of the updated resource.  Acts like an UPDATE.
1171         //
1172         if (neededSync == true) {
1173                 result = payloadOut.getBytes();
1174         } else {
1175                 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1176                                 itemIdentifier).getBytes();
1177                 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1178             throw new CSWebApplicationException(response);
1179         }
1180         
1181         return result;
1182     }
1183     
1184     /**
1185      * Update authorityItem.
1186      * 
1187      * @param parentspecifier either a CSID or one of the urn forms
1188      * @param itemspecifier either a CSID or one of the urn forms
1189      *
1190      * @return the multipart output
1191      */
1192     @PUT
1193     @Path("{csid}/items/{itemcsid}")
1194     public byte[] updateAuthorityItem(
1195                 @Context ResourceMap resourceMap, 
1196             @Context UriInfo uriInfo,
1197             @PathParam("csid") String parentSpecifier,
1198             @PathParam("itemcsid") String itemSpecifier,
1199             String xmlPayload) {
1200         PoxPayloadOut result = null;
1201         
1202         try {
1203             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1204             result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1205                         AuthorityServiceUtils.UPDATE_REV,                       // passing TRUE so rev num increases, passing
1206                         AuthorityServiceUtils.NO_CHANGE);       // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1207         } catch (Exception e) {
1208             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1209         }
1210         
1211         return result.getBytes();
1212     }
1213     
1214     public PoxPayloadOut updateAuthorityItem(
1215                 ServiceContext itemServiceCtx, // Ok to be null.  Will be null on PUT calls, but not on sync calls
1216                 ResourceMap resourceMap, 
1217             UriInfo uriInfo,
1218             String parentspecifier,
1219             String itemspecifier,
1220             PoxPayloadIn theUpdate,
1221             boolean shouldUpdateRevNumber,
1222             Boolean isProposed) throws Exception {
1223         PoxPayloadOut result = null;
1224         
1225         CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
1226         String parentcsid = csidAndShortId.CSID;
1227         String parentShortId = csidAndShortId.shortIdentifier;
1228         //
1229         // If the itemServiceCtx context is not null, use it.  Otherwise, create a new context
1230         //
1231         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
1232         if (ctx == null) {
1233                 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
1234         } else {
1235                 ctx.setInput(theUpdate); // the update payload
1236         }
1237         
1238         String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
1239
1240         // We omit the parentShortId, only needed when doing a create...
1241         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
1242         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1243         if (isProposed != null) {
1244                 handler.setIsProposed(isProposed);
1245         }
1246         getRepositoryClient(ctx).update(ctx, itemcsid, handler);
1247         result = ctx.getOutput();
1248
1249         return result;
1250     }
1251
1252     /**
1253      * Delete authorityItem.
1254      * 
1255      * @param parentcsid the parentcsid
1256      * @param itemcsid the itemcsid
1257      * 
1258      * @return the response
1259      */
1260     @DELETE
1261     @Path("{csid}/items/{itemcsid}")
1262     public Response deleteAuthorityItem(
1263             @PathParam("csid") String parentcsid,
1264             @PathParam("itemcsid") String itemcsid) {
1265         Response result = null;
1266         //try{
1267         if (logger.isDebugEnabled()) {
1268             logger.debug("deleteAuthorityItem with parentcsid=" + parentcsid + " and itemcsid=" + itemcsid);
1269         }
1270         
1271         try {
1272             deleteAuthorityItem(null, parentcsid, itemcsid);
1273             result = Response.status(HttpResponseCodes.SC_OK).build();
1274         } catch (Exception e) {
1275             throw bigReThrow(e, ServiceMessages.DELETE_FAILED + "  itemcsid: " + itemcsid + " parentcsid:" + parentcsid);
1276         }
1277             //Laramie, removing this catch, since it will surely fail below, since itemcsid or parentcsid will be null.
1278         return result;
1279     }
1280             // }catch (Throwable t){
1281     public void deleteAuthorityItem(ServiceContext existingCtx,
1282             String parentcsid,
1283             String itemcsid) throws Exception {
1284         Response result = null;
1285             //    System.out.println("ERROR in setting up DELETE: "+t);
1286         ensureCSID(parentcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1287         ensureCSID(itemcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1288             // }
1289             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName());
1290         if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1291                 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Use existing repo session if one exists
1292                 ctx.setProperties(existingCtx.getProperties());
1293         }
1294             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
1295             getRepositoryClient(ctx).delete(ctx, itemcsid, handler);
1296         }
1297
1298     @GET
1299     @Path("{csid}/items/{itemcsid}/" + hierarchy)
1300     @Produces("application/xml")
1301     public String getHierarchy(@PathParam("csid") String csid,
1302             @PathParam("itemcsid") String itemcsid,
1303             @Context UriInfo ui) throws Exception {
1304         try {
1305             // 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...?
1306             String calledUri = ui.getPath();
1307             String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1308             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), ui);
1309             ctx.setUriInfo(ui);
1310             String direction = ui.getQueryParameters().getFirst(Hierarchy.directionQP);
1311             if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1312                 return Hierarchy.surface(ctx, itemcsid, uri);
1313             } else {
1314                 return Hierarchy.dive(ctx, itemcsid, uri);
1315             }
1316         } catch (Exception e) {
1317             throw bigReThrow(e, "Error showing hierarchy", itemcsid);
1318         }
1319     }
1320     
1321     protected String getItemDocType(String tenantId) {
1322         return getDocType(tenantId, getItemServiceName());
1323     }
1324         
1325     /**
1326      * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1327      * for the current resource, for all tenants
1328      * 
1329      * @return a map of URI templates for the current resource, for all tenants
1330      */
1331     @Override
1332     public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1333         Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1334                 super.getUriRegistryEntries();
1335         List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1336         for (String tenantId : tenantIds) {
1337                 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1338         }
1339         return uriRegistryEntriesMap;
1340     }
1341   
1342 }