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