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