]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
c6bb8652ed8dfac6968883f63336fdebf8909c04
[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             //
760             ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName());
761             AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
762             targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
763             ctx.setProperty(WorkflowClient.TARGET_DOCHANDLER, targetDocHandler); //added as a context param for the workflow document handler -it will call the parent's dochandler "prepareForWorkflowTranstion" method
764             //
765             // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
766             //
767             String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
768             ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
769             
770                 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
771             TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
772             ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
773             
774             WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
775             getRepositoryClient(ctx).update(ctx, itemcsid, handler);
776             result = ctx.getOutput();
777         } catch (Exception e) {
778             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
779         }
780         
781         return result;
782     }
783     
784     private PoxPayloadOut getAuthorityItem(
785                 ServiceContext ctx,
786             String parentIdentifier,
787             String itemIdentifier) throws Exception {
788         PoxPayloadOut result = null;
789         
790         String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
791         // We omit the parentShortId, only needed when doing a create...
792         DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
793
794         Specifier itemSpec = getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
795         if (itemSpec.form == SpecifierForm.CSID) {
796             getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
797         } else {
798             String itemWhereClause =
799                         RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
800             DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
801             handler.setDocumentFilter(myFilter);
802             getRepositoryClient(ctx).get(ctx, handler);
803         }
804         // TODO should we assert that the item is in the passed vocab?
805         result = (PoxPayloadOut) ctx.getOutput();
806
807         return result;
808     }
809
810     public PoxPayloadOut getAuthorityItemWithParentContext(
811                 ServiceContext parentCtx,
812             String parentIdentifier,
813             String itemIdentifier) throws Exception {
814         PoxPayloadOut result = null;
815         
816         ServiceContext ctx = createServiceContext(getItemServiceName(), parentCtx.getResourceMap(), parentCtx.getUriInfo());
817         if (parentCtx.getCurrentRepositorySession() != null) {
818                 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
819         }
820         result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
821         
822         return result;
823     }
824     
825     /**
826      * Gets the authority item.
827      * 
828      * @param parentspecifier either a CSID or one of the urn forms
829      * @param itemspecifier either a CSID or one of the urn forms
830      * 
831      * @return the authority item
832      */
833     @GET
834     @Path("{csid}/items/{itemcsid}")
835     public byte[] getAuthorityItem(
836             @Context Request request,
837             @Context UriInfo uriInfo,
838                 @Context ResourceMap resourceMap,            
839             @PathParam("csid") String parentIdentifier,
840             @PathParam("itemcsid") String itemIdentifier) {
841         PoxPayloadOut result = null;
842         try {
843             RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = 
844                         (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
845
846             JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
847             ctx.setJaxRsContext(jaxRsContext);
848             
849             result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
850         } catch (DocumentNotFoundException dnf) {
851             throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
852         } catch (Exception e) {
853             throw bigReThrow(e, ServiceMessages.GET_FAILED);
854         }
855                 
856         return result.getBytes();
857     }
858
859     /*
860      * Most of the authority child classes will/should use this implementation.  However, the Vocabulary service's item schema is
861      * different enough that it will have to override this method in it's resource class.
862      */
863     @Override
864         protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
865                 String result = null;
866
867                 result = NuxeoUtils.getPrimaryElPathPropertyName(
868                                 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
869                                 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
870
871                 return result;
872         }
873         
874     @Override
875         protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
876                 String result = null;
877                 
878                 result = NuxeoUtils.getMultiElPathPropertyName(
879                                 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
880                                 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
881
882                 return result;
883         }
884     
885     /**
886      * Gets the authorityItem list for the specified authority
887      * If partialPerm is specified, keywords will be ignored.
888      * 
889      * @param specifier either a CSID or one of the urn forms
890      * @param partialTerm if non-null, matches partial terms
891      * @param keywords if non-null, matches terms in the keyword index for items
892      * @param ui passed to include additional parameters, like pagination controls
893      * 
894      * @return the authorityItem list
895      */
896     @GET
897     @Path("{csid}/items")
898     @Produces("application/xml")
899     public AbstractCommonList getAuthorityItemList(@PathParam("csid") String specifier,
900             @Context UriInfo uriInfo) {
901         AbstractCommonList result = null;
902         
903         try {
904             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
905             MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
906             
907             String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
908             String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
909             String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
910             String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
911             String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
912
913             // For the wildcard case, parentcsid is null, but docHandler will deal with this.
914             // We omit the parentShortId, only needed when doing a create...
915             String parentcsid = PARENT_WILDCARD.equals(specifier) ? null :
916                                 lookupParentCSID(specifier, "getAuthorityItemList", "LIST", uriInfo);
917             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
918                 createItemDocumentHandler(ctx, parentcsid, null);
919             
920             DocumentFilter myFilter = handler.getDocumentFilter();
921             // If we are not wildcarding the parent, add a restriction
922             if (parentcsid != null) {
923                     myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
924                             + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
925                             + "'" + parentcsid + "'",
926                             IQueryManager.SEARCH_QUALIFIER_AND);
927             }
928
929             if (Tools.notBlank(termStatus)) {
930                 // Start with the qualified termStatus field
931                 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
932                         + AuthorityItemJAXBSchema.TERM_STATUS;
933                 String[] filterTerms = termStatus.trim().split("\\|");
934                 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
935                 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
936             }
937
938             result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);            
939         } catch (Exception e) {
940             throw bigReThrow(e, ServiceMessages.LIST_FAILED);
941         }
942         
943         return result;
944     }
945
946     /**
947      * @return the name of the property used to specify references for items in this type of
948      * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
949      * Some types (like Vocabulary) use a separate property.
950      */
951     protected String getRefPropName() {
952         return ServiceBindingUtils.AUTH_REF_PROP;
953     }
954     
955     /**
956      * Gets the entities referencing this Authority item instance. The service type
957      * can be passed as a query param "type", and must match a configured type
958      * for the service bindings. If not set, the type defaults to
959      * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
960      *
961      * @param parentspecifier either a CSID or one of the urn forms
962      * @param itemspecifier either a CSID or one of the urn forms
963      * @param ui the ui
964      * 
965      * @return the info for the referencing objects
966      */
967     @GET
968     @Path("{csid}/items/{itemcsid}/refObjs")
969     @Produces("application/xml")
970     public AuthorityRefDocList getReferencingObjects(
971             @PathParam("csid") String parentspecifier,
972             @PathParam("itemcsid") String itemspecifier,
973             @Context UriTemplateRegistry uriTemplateRegistry,
974             @Context UriInfo uriInfo) {
975         AuthorityRefDocList authRefDocList = null;
976         try {
977             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
978             MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
979
980             String parentcsid = lookupParentCSID(parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
981             String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
982
983             List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
984             if(serviceTypes == null || serviceTypes.isEmpty()) {
985                 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
986             }
987             
988             // Note that we have to create the service context for the Items, not the main service
989             // We omit the parentShortId, only needed when doing a create...
990             AuthorityItemDocumentModelHandler<?> handler = (AuthorityItemDocumentModelHandler<?>)
991                                                                                                 createItemDocumentHandler(ctx, parentcsid, null);
992
993             authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
994         } catch (Exception e) {
995             throw bigReThrow(e, ServiceMessages.GET_FAILED);
996         }
997         if (authRefDocList == null) {
998             Response response = Response.status(Response.Status.NOT_FOUND).entity(
999                     "Get failed, the requested Item CSID:" + itemspecifier + ": was not found.").type(
1000                     "text/plain").build();
1001             throw new CSWebApplicationException(response);
1002         }
1003         return authRefDocList;
1004     }
1005
1006     /**
1007      * Gets the authority terms used in the indicated Authority item.
1008      *
1009      * @param parentspecifier either a CSID or one of the urn forms
1010      * @param itemspecifier either a CSID or one of the urn forms
1011      * @param ui passed to include additional parameters, like pagination controls
1012      *
1013      * @return the authority refs for the Authority item.
1014      */
1015     @GET
1016     @Path("{csid}/items/{itemcsid}/authorityrefs")
1017     @Produces("application/xml")
1018     public AuthorityRefList getAuthorityItemAuthorityRefs(
1019             @PathParam("csid") String parentspecifier,
1020             @PathParam("itemcsid") String itemspecifier,
1021             @Context UriInfo uriInfo) {
1022         AuthorityRefList authRefList = null;
1023         try {
1024             // Note that we have to create the service context for the Items, not the main service
1025             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1026             MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1027             String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1028             // We omit the parentShortId, only needed when doing a create...
1029             DocumentModelHandler<?, AbstractCommonList> handler =
1030                     (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1031
1032             String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1033
1034             List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1035             authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1036         } catch (Exception e) {
1037             throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1038         }
1039         return authRefList;
1040     }
1041     
1042     /**
1043      * Synchronizes a local authority item with a share authority server (SAS) item.
1044      * @param ctx
1045      * @param parentIdentifier
1046      * @param itemIdentifier
1047      * @return
1048      * @throws Exception
1049      */
1050     protected PoxPayloadOut synchronizeItem(
1051                 ServiceContext ctx,
1052             String parentIdentifier,
1053             String itemIdentifier) throws Exception {
1054         PoxPayloadOut result = null;
1055         AuthorityItemSpecifier specifier;
1056         CsidAndShortIdentifier parent;
1057         boolean neededSync = false;
1058
1059         parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1060         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1061         Specifier parentSpecifier = getSpecifier(parent.CSID, "getAuthority", "GET");
1062         Specifier itemSpecifier = getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1063         specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1064         //
1065         neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1066         if (neededSync == true) {
1067                 result = (PoxPayloadOut) ctx.getOutput();
1068         }
1069         
1070         return result;
1071     }
1072
1073     /**
1074      * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1075      * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1076      * local authority.  The parent context was created for the authority (parent) because the sync started there.
1077      * @param parentCtx
1078      * @param parentIdentifier
1079      * @param itemIdentifier
1080      * @return
1081      * @throws Exception
1082      */
1083     public PoxPayloadOut synchronizeItemWithParentContext(
1084                 ServiceContext parentCtx,
1085             String parentIdentifier,
1086             String itemIdentifier
1087             ) throws Exception {
1088         PoxPayloadOut result = null;
1089         
1090         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1091                         parentCtx.getResourceMap(),
1092                         parentCtx.getUriInfo());
1093         if (parentCtx.getCurrentRepositorySession() != null) {
1094                 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
1095         }
1096         result = synchronizeItem(ctx, parentIdentifier, itemIdentifier);
1097         
1098         return result;
1099     }
1100     
1101     /**
1102      * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1103      * 
1104      * @param specifier either CSIDs and/or one of the urn forms
1105      * 
1106      * @return the authority item if it was synchronized with SAS
1107      */
1108     @GET
1109     @Path("{csid}/items/{itemcsid}/sync")
1110     public byte[] synchronizeItem(
1111                 @Context ResourceMap resourceMap,
1112             @Context UriInfo uriInfo,
1113             @PathParam("csid") String parentIdentifier,
1114             @PathParam("itemcsid") String itemIdentifier) {
1115         byte[] result;
1116         boolean neededSync = false;
1117         PoxPayloadOut payloadOut = null;
1118         
1119         try {
1120             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1121             payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier);
1122             if (payloadOut != null) {
1123                 neededSync = true;
1124             }
1125         } catch (Exception e) {
1126             throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1127         }
1128
1129         //
1130         // If a sync was needed and was successful, return a copy of the updated resource.  Acts like an UPDATE.
1131         //
1132         if (neededSync == true) {
1133                 result = payloadOut.getBytes();
1134         } else {
1135                 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1136                                 itemIdentifier).getBytes();
1137                 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1138             throw new CSWebApplicationException(response);
1139         }
1140         
1141         return result;
1142     }
1143     
1144     /**
1145      * Update authorityItem.
1146      * 
1147      * @param parentspecifier either a CSID or one of the urn forms
1148      * @param itemspecifier either a CSID or one of the urn forms
1149      *
1150      * @return the multipart output
1151      */
1152     @PUT
1153     @Path("{csid}/items/{itemcsid}")
1154     public byte[] updateAuthorityItem(
1155                 @Context ResourceMap resourceMap, 
1156             @Context UriInfo uriInfo,
1157             @PathParam("csid") String parentSpecifier,
1158             @PathParam("itemcsid") String itemSpecifier,
1159             String xmlPayload) {
1160         PoxPayloadOut result = null;
1161         
1162         try {
1163             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1164             result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1165                         AuthorityServiceUtils.UPDATE_REV); // passing TRUE so rev num increases
1166         } catch (Exception e) {
1167             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1168         }
1169         
1170         return result.getBytes();
1171     }
1172     
1173     public PoxPayloadOut updateAuthorityItem(
1174                 ServiceContext itemServiceCtx, // Ok to be null.  Will be null on PUT calls, but not on sync calls
1175                 ResourceMap resourceMap, 
1176             UriInfo uriInfo,
1177             String parentspecifier,
1178             String itemspecifier,
1179             PoxPayloadIn theUpdate,
1180             boolean shouldUpdateRevNumber) throws Exception {
1181         PoxPayloadOut result = null;
1182         
1183         CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
1184         String parentcsid = csidAndShortId.CSID;
1185         String parentShortId = csidAndShortId.shortIdentifier;
1186         //
1187         // If the itemServiceCtx context is not null, use it.  Otherwise, create a new context
1188         //
1189         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
1190         if (ctx == null) {
1191                 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
1192         } else {
1193                 ctx.setInput(theUpdate); // the update payload
1194         }
1195         
1196         String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
1197
1198         // We omit the parentShortId, only needed when doing a create...
1199         AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
1200         handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1201         getRepositoryClient(ctx).update(ctx, itemcsid, handler);
1202         result = ctx.getOutput();
1203
1204         return result;
1205     }
1206
1207     /**
1208      * Delete authorityItem.
1209      * 
1210      * @param parentcsid the parentcsid
1211      * @param itemcsid the itemcsid
1212      * 
1213      * @return the response
1214      */
1215     @DELETE
1216     @Path("{csid}/items/{itemcsid}")
1217     public Response deleteAuthorityItem(
1218             @PathParam("csid") String parentcsid,
1219             @PathParam("itemcsid") String itemcsid) {
1220         //try{
1221         if (logger.isDebugEnabled()) {
1222             logger.debug("deleteAuthorityItem with parentcsid=" + parentcsid + " and itemcsid=" + itemcsid);
1223         }
1224         try {
1225             ensureCSID(parentcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1226             ensureCSID(itemcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1227             //Laramie, removing this catch, since it will surely fail below, since itemcsid or parentcsid will be null.
1228             // }catch (Throwable t){
1229             //    System.out.println("ERROR in setting up DELETE: "+t);
1230             // }
1231             // try {
1232             // Note that we have to create the service context for the Items, not the main service
1233             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName());
1234             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
1235             getRepositoryClient(ctx).delete(ctx, itemcsid, handler);
1236             return Response.status(HttpResponseCodes.SC_OK).build();
1237         } catch (Exception e) {
1238             throw bigReThrow(e, ServiceMessages.DELETE_FAILED + "  itemcsid: " + itemcsid + " parentcsid:" + parentcsid);
1239         }
1240     }
1241     public final static String hierarchy = "hierarchy";
1242
1243     @GET
1244     @Path("{csid}/items/{itemcsid}/" + hierarchy)
1245     @Produces("application/xml")
1246     public String getHierarchy(@PathParam("csid") String csid,
1247             @PathParam("itemcsid") String itemcsid,
1248             @Context UriInfo ui) throws Exception {
1249         try {
1250             // 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...?
1251             String calledUri = ui.getPath();
1252             String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1253             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), ui);
1254             ctx.setUriInfo(ui);
1255             String direction = ui.getQueryParameters().getFirst(Hierarchy.directionQP);
1256             if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1257                 return Hierarchy.surface(ctx, itemcsid, uri);
1258             } else {
1259                 return Hierarchy.dive(ctx, itemcsid, uri);
1260             }
1261         } catch (Exception e) {
1262             throw bigReThrow(e, "Error showing hierarchy", itemcsid);
1263         }
1264     }
1265     
1266     protected String getItemDocType(String tenantId) {
1267         return getDocType(tenantId, getItemServiceName());
1268     }
1269         
1270     /**
1271      * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1272      * for the current resource, for all tenants
1273      * 
1274      * @return a map of URI templates for the current resource, for all tenants
1275      */
1276     @Override
1277     public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1278         Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1279                 super.getUriRegistryEntries();
1280         List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1281         for (String tenantId : tenantIds) {
1282                 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1283         }
1284         return uriRegistryEntriesMap;
1285     }
1286   
1287 }