]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
f8dcd8215d14abfe3e2ad7ae5e34f9ad05610b5d
[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             result = updateItemWorkflowWithTransition(null, // Ok to send null
724                         csid, itemcsid, transition, AuthorityServiceUtils.UPDATE_REV);
725         } catch (Exception e) {
726             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
727         }
728         
729         return result.getBytes();
730     }
731     
732     /**
733      * Update an authority item's workflow state.
734      * @param existingContext
735      * @param csid
736      * @param itemcsid
737      * @param transition
738      * @return
739      */
740     public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext existingContext,
741             String csid,
742             String itemcsid,
743             String transition,
744             boolean updateRevNumber) {
745         PoxPayloadOut result = null;
746         
747         try {
748                 //
749                 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
750                 //
751                 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(), 
752                                 WorkflowClient.SERVICE_COMMONPART_NAME);
753             MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
754             if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
755                 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If a repo session is already open, we need to use it and not create a new one
756             }
757
758             // Create a service context and document handler for the target resource -not the workflow resource itself.
759             ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName());
760             AuthorityItemDocumentModelHandler parentDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
761             parentDocHandler.setShouldUpdateRevNumber(updateRevNumber);
762             ctx.setProperty(WorkflowClient.TARGET_DOCHANDLER, parentDocHandler); //added as a context param for the workflow document handler -it will call the parent's dochandler "prepareForWorkflowTranstion" method
763
764             // When looking for the document, we need to use the parent resource's workspace name -not the "workflow" workspace name
765             String parentWorkspaceName = targetCtx.getRepositoryWorkspaceName();
766             ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
767             
768                 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
769             TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
770             ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
771             
772             WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
773             getRepositoryClient(ctx).update(ctx, itemcsid, handler);
774             result = ctx.getOutput();
775         } catch (Exception e) {
776             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
777         }
778         
779         return result;
780     }
781     
782     private PoxPayloadOut getAuthorityItem(
783                 ServiceContext ctx,
784             String parentIdentifier,
785             String itemIdentifier) throws Exception {
786         PoxPayloadOut result = null;
787         
788         String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
789         // We omit the parentShortId, only needed when doing a create...
790         DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
791
792         Specifier itemSpec = getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
793         if (itemSpec.form == SpecifierForm.CSID) {
794             getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
795         } else {
796             String itemWhereClause =
797                         RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
798             DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
799             handler.setDocumentFilter(myFilter);
800             getRepositoryClient(ctx).get(ctx, handler);
801         }
802         // TODO should we assert that the item is in the passed vocab?
803         result = (PoxPayloadOut) ctx.getOutput();
804
805         return result;
806     }
807
808     public PoxPayloadOut getAuthorityItemWithParentContext(
809                 ServiceContext parentCtx,
810             String parentIdentifier,
811             String itemIdentifier) throws Exception {
812         PoxPayloadOut result = null;
813         
814         ServiceContext ctx = createServiceContext(getItemServiceName(), parentCtx.getResourceMap(), parentCtx.getUriInfo());
815         if (parentCtx.getCurrentRepositorySession() != null) {
816                 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
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      * @return the authorityItem list
893      */
894     @GET
895     @Path("{csid}/items")
896     @Produces("application/xml")
897     public AbstractCommonList getAuthorityItemList(@PathParam("csid") String specifier,
898             @Context UriInfo uriInfo) {
899         AbstractCommonList result = null;
900         
901         try {
902             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
903             MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
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         } catch (Exception e) {
938             throw bigReThrow(e, ServiceMessages.LIST_FAILED);
939         }
940         
941         return result;
942     }
943
944     /**
945      * @return the name of the property used to specify references for items in this type of
946      * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
947      * Some types (like Vocabulary) use a separate property.
948      */
949     protected String getRefPropName() {
950         return ServiceBindingUtils.AUTH_REF_PROP;
951     }
952     
953     /**
954      * Gets the entities referencing this Authority item instance. The service type
955      * can be passed as a query param "type", and must match a configured type
956      * for the service bindings. If not set, the type defaults to
957      * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
958      *
959      * @param parentspecifier either a CSID or one of the urn forms
960      * @param itemspecifier either a CSID or one of the urn forms
961      * @param ui the ui
962      * 
963      * @return the info for the referencing objects
964      */
965     @GET
966     @Path("{csid}/items/{itemcsid}/refObjs")
967     @Produces("application/xml")
968     public AuthorityRefDocList getReferencingObjects(
969             @PathParam("csid") String parentspecifier,
970             @PathParam("itemcsid") String itemspecifier,
971             @Context UriTemplateRegistry uriTemplateRegistry,
972             @Context UriInfo uriInfo) {
973         AuthorityRefDocList authRefDocList = null;
974         try {
975             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
976             MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
977
978             String parentcsid = lookupParentCSID(parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
979             String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
980
981             List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
982             if(serviceTypes == null || serviceTypes.isEmpty()) {
983                 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
984             }
985             
986             // Note that we have to create the service context for the Items, not the main service
987             // We omit the parentShortId, only needed when doing a create...
988             AuthorityItemDocumentModelHandler<?> handler = (AuthorityItemDocumentModelHandler<?>)
989                                                                                                 createItemDocumentHandler(ctx, parentcsid, null);
990
991             authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
992         } catch (Exception e) {
993             throw bigReThrow(e, ServiceMessages.GET_FAILED);
994         }
995         if (authRefDocList == null) {
996             Response response = Response.status(Response.Status.NOT_FOUND).entity(
997                     "Get failed, the requested Item CSID:" + itemspecifier + ": was not found.").type(
998                     "text/plain").build();
999             throw new CSWebApplicationException(response);
1000         }
1001         return authRefDocList;
1002     }
1003
1004     /**
1005      * Gets the authority terms used in the indicated Authority item.
1006      *
1007      * @param parentspecifier either a CSID or one of the urn forms
1008      * @param itemspecifier either a CSID or one of the urn forms
1009      * @param ui passed to include additional parameters, like pagination controls
1010      *
1011      * @return the authority refs for the Authority item.
1012      */
1013     @GET
1014     @Path("{csid}/items/{itemcsid}/authorityrefs")
1015     @Produces("application/xml")
1016     public AuthorityRefList getAuthorityItemAuthorityRefs(
1017             @PathParam("csid") String parentspecifier,
1018             @PathParam("itemcsid") String itemspecifier,
1019             @Context UriInfo uriInfo) {
1020         AuthorityRefList authRefList = null;
1021         try {
1022             // Note that we have to create the service context for the Items, not the main service
1023             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1024             MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1025             String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1026             // We omit the parentShortId, only needed when doing a create...
1027             DocumentModelHandler<?, AbstractCommonList> handler =
1028                     (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1029
1030             String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1031
1032             List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1033             authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1034         } catch (Exception e) {
1035             throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1036         }
1037         return authRefList;
1038     }
1039     
1040     /**
1041      * Synchronizes a local authority item with a share authority server (SAS) item.
1042      * @param ctx
1043      * @param parentIdentifier
1044      * @param itemIdentifier
1045      * @return
1046      * @throws Exception
1047      */
1048     protected PoxPayloadOut synchronizeItem(
1049                 ServiceContext ctx,
1050             String parentIdentifier,
1051             String itemIdentifier) throws Exception {
1052         PoxPayloadOut result = null;
1053         AuthorityItemSpecifier specifier;
1054         CsidAndShortIdentifier parent;
1055         boolean neededSync = false;
1056
1057         parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1058         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1059         Specifier parentSpecifier = getSpecifier(parent.CSID, "getAuthority", "GET");
1060         Specifier itemSpecifier = getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1061         specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1062         //
1063         neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1064         if (neededSync == true) {
1065                 result = (PoxPayloadOut) ctx.getOutput();
1066         }
1067         
1068         return result;
1069     }
1070
1071     /**
1072      * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1073      * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1074      * local authority.  The parent context was created for the authority (parent) because the sync started there.
1075      * @param parentCtx
1076      * @param parentIdentifier
1077      * @param itemIdentifier
1078      * @return
1079      * @throws Exception
1080      */
1081     public PoxPayloadOut synchronizeItemWithParentContext(
1082                 ServiceContext parentCtx,
1083             String parentIdentifier,
1084             String itemIdentifier
1085             ) throws Exception {
1086         PoxPayloadOut result = null;
1087         
1088         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1089                         parentCtx.getResourceMap(),
1090                         parentCtx.getUriInfo());
1091         if (parentCtx.getCurrentRepositorySession() != null) {
1092                 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
1093         }
1094         result = synchronizeItem(ctx, parentIdentifier, itemIdentifier);
1095         
1096         return result;
1097     }
1098     
1099     /**
1100      * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1101      * 
1102      * @param specifier either CSIDs and/or one of the urn forms
1103      * 
1104      * @return the authority item if it was synchronized with SAS
1105      */
1106     @GET
1107     @Path("{csid}/items/{itemcsid}/sync")
1108     public byte[] synchronizeItem(
1109                 @Context ResourceMap resourceMap,
1110             @Context UriInfo uriInfo,
1111             @PathParam("csid") String parentIdentifier,
1112             @PathParam("itemcsid") String itemIdentifier) {
1113         byte[] result;
1114         boolean neededSync = false;
1115         PoxPayloadOut payloadOut = null;
1116         
1117         try {
1118             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1119             payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier);
1120             if (payloadOut != null) {
1121                 neededSync = true;
1122             }
1123         } catch (Exception e) {
1124             throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1125         }
1126
1127         //
1128         // If a sync was needed and was successful, return a copy of the updated resource.  Acts like an UPDATE.
1129         //
1130         if (neededSync == true) {
1131                 result = payloadOut.getBytes();
1132         } else {
1133                 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1134                                 itemIdentifier).getBytes();
1135                 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1136             throw new CSWebApplicationException(response);
1137         }
1138         
1139         return result;
1140     }
1141     
1142     /**
1143      * Update authorityItem.
1144      * 
1145      * @param parentspecifier either a CSID or one of the urn forms
1146      * @param itemspecifier either a CSID or one of the urn forms
1147      *
1148      * @return the multipart output
1149      */
1150     @PUT
1151     @Path("{csid}/items/{itemcsid}")
1152     public byte[] updateAuthorityItem(
1153                 @Context ResourceMap resourceMap, 
1154             @Context UriInfo uriInfo,
1155             @PathParam("csid") String parentSpecifier,
1156             @PathParam("itemcsid") String itemSpecifier,
1157             String xmlPayload) {
1158         PoxPayloadOut result = null;
1159         
1160         try {
1161             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1162             result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1163                         AuthorityServiceUtils.UPDATE_REV); // passing TRUE so rev num increases
1164         } catch (Exception e) {
1165             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1166         }
1167         
1168         return result.getBytes();
1169     }
1170     
1171     public PoxPayloadOut updateAuthorityItem(
1172                 ServiceContext itemServiceCtx, // Ok to be null.  Will be null on PUT calls, but not on sync calls
1173                 ResourceMap resourceMap, 
1174             UriInfo uriInfo,
1175             String parentspecifier,
1176             String itemspecifier,
1177             PoxPayloadIn theUpdate,
1178             boolean shouldUpdateRevNumber) throws Exception {
1179         PoxPayloadOut result = null;
1180         
1181         CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
1182         String parentcsid = csidAndShortId.CSID;
1183         String parentShortId = csidAndShortId.shortIdentifier;
1184         //
1185         // If the itemServiceCtx context is not null, use it.  Otherwise, create a new context
1186         //
1187         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
1188         if (ctx == null) {
1189                 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
1190         } else {
1191                 ctx.setInput(theUpdate); // the update payload
1192         }
1193         
1194         String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
1195
1196         // We omit the parentShortId, only needed when doing a create...
1197         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
1198         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1199         getRepositoryClient(ctx).update(ctx, itemcsid, handler);
1200         result = ctx.getOutput();
1201
1202         return result;
1203     }
1204
1205     /**
1206      * Delete authorityItem.
1207      * 
1208      * @param parentcsid the parentcsid
1209      * @param itemcsid the itemcsid
1210      * 
1211      * @return the response
1212      */
1213     @DELETE
1214     @Path("{csid}/items/{itemcsid}")
1215     public Response deleteAuthorityItem(
1216             @PathParam("csid") String parentcsid,
1217             @PathParam("itemcsid") String itemcsid) {
1218         //try{
1219         if (logger.isDebugEnabled()) {
1220             logger.debug("deleteAuthorityItem with parentcsid=" + parentcsid + " and itemcsid=" + itemcsid);
1221         }
1222         try {
1223             ensureCSID(parentcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1224             ensureCSID(itemcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1225             //Laramie, removing this catch, since it will surely fail below, since itemcsid or parentcsid will be null.
1226             // }catch (Throwable t){
1227             //    System.out.println("ERROR in setting up DELETE: "+t);
1228             // }
1229             // try {
1230             // Note that we have to create the service context for the Items, not the main service
1231             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName());
1232             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
1233             getRepositoryClient(ctx).delete(ctx, itemcsid, handler);
1234             return Response.status(HttpResponseCodes.SC_OK).build();
1235         } catch (Exception e) {
1236             throw bigReThrow(e, ServiceMessages.DELETE_FAILED + "  itemcsid: " + itemcsid + " parentcsid:" + parentcsid);
1237         }
1238     }
1239     public final static String hierarchy = "hierarchy";
1240
1241     @GET
1242     @Path("{csid}/items/{itemcsid}/" + hierarchy)
1243     @Produces("application/xml")
1244     public String getHierarchy(@PathParam("csid") String csid,
1245             @PathParam("itemcsid") String itemcsid,
1246             @Context UriInfo ui) throws Exception {
1247         try {
1248             // 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...?
1249             String calledUri = ui.getPath();
1250             String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1251             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), ui);
1252             ctx.setUriInfo(ui);
1253             String direction = ui.getQueryParameters().getFirst(Hierarchy.directionQP);
1254             if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1255                 return Hierarchy.surface(ctx, itemcsid, uri);
1256             } else {
1257                 return Hierarchy.dive(ctx, itemcsid, uri);
1258             }
1259         } catch (Exception e) {
1260             throw bigReThrow(e, "Error showing hierarchy", itemcsid);
1261         }
1262     }
1263     
1264     protected String getItemDocType(String tenantId) {
1265         return getDocType(tenantId, getItemServiceName());
1266     }
1267         
1268     /**
1269      * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1270      * for the current resource, for all tenants
1271      * 
1272      * @return a map of URI templates for the current resource, for all tenants
1273      */
1274     @Override
1275     public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1276         Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1277                 super.getUriRegistryEntries();
1278         List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1279         for (String tenantId : tenantIds) {
1280                 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1281         }
1282         return uriRegistryEntriesMap;
1283     }
1284   
1285 }