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