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