]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
f72d750c52106fbae2508bc99c0fe2a18f6b9db5
[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 org.collectionspace.services.client.IClientQueryParams;
27 import org.collectionspace.services.client.IQueryManager;
28 import org.collectionspace.services.client.PoxPayloadIn;
29 import org.collectionspace.services.client.PoxPayloadOut;
30 import org.collectionspace.services.client.workflow.WorkflowClient;
31 import org.collectionspace.services.common.ClientType;
32 import org.collectionspace.services.common.ResourceBase;
33 import org.collectionspace.services.common.ResourceMap;
34 import org.collectionspace.services.common.ServiceMain;
35 import org.collectionspace.services.common.ServiceMessages;
36 import org.collectionspace.services.common.XmlTools;
37 import org.collectionspace.services.common.api.RefName;
38 import org.collectionspace.services.common.api.Tools;
39 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
40 import org.collectionspace.services.common.authorityref.AuthorityRefList;
41 import org.collectionspace.services.common.context.JaxRsContext;
42 import org.collectionspace.services.common.context.MultipartServiceContext;
43 import org.collectionspace.services.common.context.MultipartServiceContextImpl;
44 import org.collectionspace.services.common.context.RemoteServiceContext;
45 import org.collectionspace.services.common.context.ServiceBindingUtils;
46 import org.collectionspace.services.common.context.ServiceContext;
47 import org.collectionspace.services.common.document.DocumentException;
48 import org.collectionspace.services.common.document.DocumentFilter;
49 import org.collectionspace.services.common.document.DocumentHandler;
50 import org.collectionspace.services.common.document.DocumentNotFoundException;
51 import org.collectionspace.services.common.document.DocumentWrapper;
52 import org.collectionspace.services.common.query.QueryManager;
53 import org.collectionspace.services.common.repository.RepositoryClient;
54 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
55 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
56 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
57 import org.collectionspace.services.jaxb.AbstractCommonList;
58 import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
59 import org.collectionspace.services.relation.RelationResource;
60 import org.collectionspace.services.relation.RelationsCommonList;
61 import org.collectionspace.services.relation.RelationshipType;
62 import org.jboss.resteasy.util.HttpResponseCodes;
63 import org.nuxeo.ecm.core.api.DocumentModel;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67 import javax.ws.rs.Consumes;
68 import javax.ws.rs.DELETE;
69 import javax.ws.rs.GET;
70 import javax.ws.rs.POST;
71 import javax.ws.rs.PUT;
72 import javax.ws.rs.Path;
73 import javax.ws.rs.PathParam;
74 import javax.ws.rs.Produces;
75 import javax.ws.rs.QueryParam;
76 import javax.ws.rs.WebApplicationException;
77 import javax.ws.rs.core.Context;
78 import javax.ws.rs.core.MultivaluedMap;
79 import javax.ws.rs.core.Request;
80 import javax.ws.rs.core.Response;
81 import javax.ws.rs.core.UriBuilder;
82 import javax.ws.rs.core.UriInfo;
83
84 import java.util.ArrayList;
85 import java.util.List;
86
87 /**
88  * The Class AuthorityResource.
89  */
90 /**
91  * @author pschmitz
92  *
93  * @param <AuthCommon>
94  * @param <AuthItemHandler>
95  */
96 /**
97  * @author pschmitz
98  *
99  * @param <AuthCommon>
100  * @param <AuthItemHandler>
101  */
102 @Consumes("application/xml")
103 @Produces("application/xml")
104 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
105         extends ResourceBase {
106         
107         final static String SEARCH_TYPE_TERMSTATUS = "ts";
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();
114     final static String URN_PREFIX = "urn:cspace:";
115     final static int URN_PREFIX_LEN = URN_PREFIX.length();
116     final static String URN_PREFIX_NAME = "name(";
117     final static int URN_NAME_PREFIX_LEN = URN_PREFIX_LEN + URN_PREFIX_NAME.length();
118     final static String URN_PREFIX_ID = "id(";
119     final static int URN_ID_PREFIX_LEN = URN_PREFIX_LEN + URN_PREFIX_ID.length();
120     final static String FETCH_SHORT_ID = "_fetch_";
121     final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
122
123     public enum SpecifierForm {
124
125         CSID, URN_NAME
126     };
127
128     public class Specifier {
129
130         public SpecifierForm form;
131         public String value;
132
133         Specifier(SpecifierForm form, String value) {
134             this.form = form;
135             this.value = value;
136         }
137     }
138
139     protected Specifier getSpecifier(String specifierIn, String method, String op) throws WebApplicationException {
140         if (logger.isDebugEnabled()) {
141             logger.debug("getSpecifier called by: " + method + " with specifier: " + specifierIn);
142         }
143         if (specifierIn != null) {
144             if (!specifierIn.startsWith(URN_PREFIX)) {
145                 // We'll assume it is a CSID and complain if it does not match
146                 return new Specifier(SpecifierForm.CSID, specifierIn);
147             } else {
148                 if (specifierIn.startsWith(URN_PREFIX_NAME, URN_PREFIX_LEN)) {
149                     int closeParen = specifierIn.indexOf(')', URN_NAME_PREFIX_LEN);
150                     if (closeParen >= 0) {
151                         return new Specifier(SpecifierForm.URN_NAME,
152                                 specifierIn.substring(URN_NAME_PREFIX_LEN, closeParen));
153                     }
154                 } else if (specifierIn.startsWith(URN_PREFIX_ID, URN_PREFIX_LEN)) {
155                     int closeParen = specifierIn.indexOf(')', URN_ID_PREFIX_LEN);
156                     if (closeParen >= 0) {
157                         return new Specifier(SpecifierForm.CSID,
158                                 specifierIn.substring(URN_ID_PREFIX_LEN, closeParen));
159                     }
160                 }
161             }
162         }
163         logger.error(method + ": bad or missing specifier!");
164         Response response = Response.status(Response.Status.BAD_REQUEST).entity(
165                 op + " failed on bad or missing Authority specifier").type(
166                 "text/plain").build();
167         throw new WebApplicationException(response);
168     }
169
170     /**
171      * Instantiates a new Authority resource.
172      */
173     public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
174             String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
175         this.authCommonClass = authCommonClass;
176         this.resourceClass = resourceClass;
177         this.authorityCommonSchemaName = authorityCommonSchemaName;
178         this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
179     }
180
181     public abstract String getItemServiceName();
182
183     @Override
184     protected String getVersionString() {
185         return "$LastChangedRevision: 2617 $";
186     }
187
188     @Override
189     public Class<AuthCommon> getCommonPartClass() {
190         return authCommonClass;
191     }
192
193     /**
194      * Creates the item document handler.
195      * 
196      * @param ctx the ctx
197      * @param inAuthority the in vocabulary
198      * 
199      * @return the document handler
200      * 
201      * @throws Exception the exception
202      */
203     protected DocumentHandler createItemDocumentHandler(
204             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
205             String inAuthority, String parentShortIdentifier)
206             throws Exception {
207         String authorityRefNameBase;
208         AuthorityItemDocumentModelHandler<?> docHandler;
209
210         if (parentShortIdentifier == null) {
211             authorityRefNameBase = null;
212         } else {
213             ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx =
214                     createServiceContext(getServiceName());
215             if (parentShortIdentifier.equals(FETCH_SHORT_ID)) {
216                 // Get from parent document
217                 parentShortIdentifier = getAuthShortIdentifier(parentCtx, inAuthority);
218             }
219             authorityRefNameBase = buildAuthorityRefNameBase(parentCtx, parentShortIdentifier);
220         }
221
222         docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
223                 ctx.getCommonPartLabel(getItemServiceName()),
224                 authCommonClass);
225         docHandler.setInAuthority(inAuthority);
226         docHandler.setAuthorityRefNameBase(authorityRefNameBase);
227
228         return docHandler;
229     }
230
231     public String getAuthShortIdentifier(
232             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
233             throws DocumentNotFoundException, DocumentException {
234         String shortIdentifier = null;
235         try {
236             DocumentWrapper<DocumentModel> wrapDoc = getRepositoryClient(ctx).getDocFromCsid(ctx, authCSID);
237             AuthorityDocumentModelHandler<?> handler =
238                     (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
239             shortIdentifier = handler.getShortIdentifier(wrapDoc, authorityCommonSchemaName);
240         } catch (Exception e) {
241             if (logger.isDebugEnabled()) {
242                 logger.debug("Caught exception ", e);
243             }
244             throw new DocumentException(e);
245         }
246         return shortIdentifier;
247     }
248
249     protected String buildAuthorityRefNameBase(
250             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
251         RefName.Authority authority = RefName.buildAuthority(ctx.getTenantName(),
252                 ctx.getServiceName(), shortIdentifier, null);
253         return authority.toString();
254     }
255
256     public static class CsidAndShortIdentifier {
257
258         String CSID;
259         String shortIdentifier;
260     }
261
262     public String lookupParentCSID(String parentspecifier, String method, String op, MultivaluedMap<String, String> queryParams)
263             throws Exception {
264         CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(parentspecifier, method, op, queryParams);
265         return tempResult.CSID;
266     }
267
268     public CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(String parentspecifier, String method, String op, MultivaluedMap<String, String> queryParams)
269             throws Exception {
270         CsidAndShortIdentifier result = new CsidAndShortIdentifier();
271         Specifier parentSpec = getSpecifier(parentspecifier, method, op);
272         // Note that we have to create the service context for the Items, not the main service
273         String parentcsid;
274         String parentShortIdentifier;
275         if (parentSpec.form == SpecifierForm.CSID) {
276             parentShortIdentifier = null;
277             parentcsid = parentSpec.value;
278             // Uncomment when app layer is ready to integrate
279             // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
280             parentShortIdentifier = FETCH_SHORT_ID;
281         } else {
282             parentShortIdentifier = parentSpec.value;
283             String whereClause = buildWhereForAuthByName(parentSpec.value);
284             ServiceContext ctx = createServiceContext(getServiceName(), queryParams);
285             parentcsid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
286         }
287         result.CSID = parentcsid;
288         result.shortIdentifier = parentShortIdentifier;
289         return result;
290     }
291
292     public String lookupItemCSID(String itemspecifier, String parentcsid, String method, String op, ServiceContext ctx)
293             throws DocumentException {
294         String itemcsid;
295         Specifier itemSpec = getSpecifier(itemspecifier, method, op);
296         if (itemSpec.form == SpecifierForm.CSID) {
297             itemcsid = itemSpec.value;
298         } else {
299             String itemWhereClause = buildWhereForAuthItemByName(itemSpec.value, parentcsid);
300             itemcsid = getRepositoryClient(ctx).findDocCSID(ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
301         }
302         return itemcsid;
303     }
304
305     /*
306      * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then 
307      * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
308      * Resource. They then call this method on that resource.
309      */
310     @Override
311         public DocumentModel getDocModelForAuthorityItem(RefName.AuthorityItem item) 
312                         throws Exception, DocumentNotFoundException {
313         if(item == null) {
314                 return null;
315         }
316         String whereClause = buildWhereForAuthByName(item.getParentShortIdentifier());
317         // Ensure we have the right context.
318         ServiceContext ctx = createServiceContext(item.inAuthority.resource);
319         
320         String parentcsid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause);
321
322         String itemWhereClause = buildWhereForAuthItemByName(item.getShortIdentifier(), parentcsid);
323         ctx = createServiceContext(getItemServiceName());
324         DocumentWrapper<DocumentModel> docWrapper = getRepositoryClient(ctx).findDoc(ctx, itemWhereClause);
325         DocumentModel docModel = docWrapper.getWrappedObject();
326         return docModel;
327     }
328
329
330     @POST
331     public Response createAuthority(String xmlPayload) {
332         try {
333             PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
334             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
335             DocumentHandler handler = createDocumentHandler(ctx);
336             String csid = getRepositoryClient(ctx).create(ctx, handler);
337             UriBuilder path = UriBuilder.fromResource(resourceClass);
338             path.path("" + csid);
339             Response response = Response.created(path.build()).build();
340             return response;
341         } catch (Exception e) {
342             throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
343         }
344     }
345
346     protected String buildWhereForAuthByName(String name) {
347         return authorityCommonSchemaName
348                 + ":" + AuthorityJAXBSchema.SHORT_IDENTIFIER
349                 + "='" + name + "'";
350     }
351
352     protected String buildWhereForAuthItemByName(String name, String parentcsid) {
353         return authorityItemCommonSchemaName
354                 + ":" + AuthorityItemJAXBSchema.SHORT_IDENTIFIER
355                 + "='" + name + "' AND "
356                 + authorityItemCommonSchemaName + ":"
357                 + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
358                 + "'" + parentcsid + "'";
359     }
360
361     /**
362      * Gets the authority.
363      * 
364      * @param specifier either a CSID or one of the urn forms
365      * 
366      * @return the authority
367      */
368     @GET
369     @Path("{csid}")
370     @Override
371     public byte[] get( // getAuthority(
372             @Context UriInfo ui,
373             @PathParam("csid") String specifier) {
374         PoxPayloadOut result = null;
375         try {
376             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
377             DocumentHandler handler = createDocumentHandler(ctx);
378
379             Specifier spec = getSpecifier(specifier, "getAuthority", "GET");
380             if (spec.form == SpecifierForm.CSID) {
381                 if (logger.isDebugEnabled()) {
382                     logger.debug("getAuthority with csid=" + spec.value);
383                 }
384                 getRepositoryClient(ctx).get(ctx, spec.value, handler);
385             } else {
386                 String whereClause = buildWhereForAuthByName(spec.value);
387                 DocumentFilter myFilter = new DocumentFilter(whereClause, 0, 1);
388                 handler.setDocumentFilter(myFilter);
389                 getRepositoryClient(ctx).get(ctx, handler);
390             }
391             result = ctx.getOutput();
392
393         } catch (Exception e) {
394             throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
395         }
396
397         if (result == null) {
398             Response response = Response.status(Response.Status.NOT_FOUND).entity(
399                     "Get failed, the requested Authority specifier:" + specifier + ": was not found.").type(
400                     "text/plain").build();
401             throw new WebApplicationException(response);
402         }
403
404         return result.getBytes();
405     }
406
407     /**
408      * Finds and populates the authority list.
409      * 
410      * @param ui the ui
411      * 
412      * @return the authority list
413      */
414     @GET
415     @Produces("application/xml")
416     public AbstractCommonList getAuthorityList(@Context UriInfo ui) {
417         try {
418             MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
419             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(queryParams);
420             DocumentHandler handler = createDocumentHandler(ctx);
421             DocumentFilter myFilter = handler.getDocumentFilter();
422             // Need to make the default sort order for authority items
423             // be on the displayName field
424             String sortBy = queryParams.getFirst(IClientQueryParams.SORT_BY_PARAM);
425             if (sortBy == null || sortBy.isEmpty()) {
426                 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
427                         + AuthorityItemJAXBSchema.DISPLAY_NAME;
428                 myFilter.setOrderByClause(qualifiedDisplayNameField);
429             }
430             String nameQ = queryParams.getFirst("refName");
431             if (nameQ != null) {
432                 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
433             }
434             getRepositoryClient(ctx).getFiltered(ctx, handler);
435             return (AbstractCommonList) handler.getCommonPartList();
436         } catch (Exception e) {
437             throw bigReThrow(e, ServiceMessages.GET_FAILED);
438         }
439     }
440
441     /**
442      * Update authority.
443      *
444      * @param specifier the csid or id
445      *
446      * @return the multipart output
447      */
448     @PUT
449     @Path("{csid}")
450     public byte[] updateAuthority(
451             @PathParam("csid") String specifier,
452             String xmlPayload) {
453         PoxPayloadOut result = null;
454         try {
455             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
456             Specifier spec = getSpecifier(specifier, "updateAuthority", "UPDATE");
457             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
458             DocumentHandler handler = createDocumentHandler(ctx);
459             String csid;
460             if (spec.form == SpecifierForm.CSID) {
461                 csid = spec.value;
462             } else {
463                 String whereClause = buildWhereForAuthByName(spec.value);
464                 csid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause);
465             }
466             getRepositoryClient(ctx).update(ctx, csid, handler);
467             result = ctx.getOutput();
468         } catch (Exception e) {
469             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
470         }
471         return result.getBytes();
472     }
473
474     /**
475      * Delete authority.
476      * 
477      * @param csid the csid
478      * 
479      * @return the response
480      */
481     @DELETE
482     @Path("{csid}")
483     public Response deleteAuthority(@PathParam("csid") String csid) {
484         if (logger.isDebugEnabled()) {
485             logger.debug("deleteAuthority with csid=" + csid);
486         }
487         try {
488             ensureCSID(csid, ServiceMessages.DELETE_FAILED, "Authority.csid");
489             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
490             getRepositoryClient(ctx).delete(ctx, csid);
491             return Response.status(HttpResponseCodes.SC_OK).build();
492         } catch (Exception e) {
493             throw bigReThrow(e, ServiceMessages.DELETE_FAILED, csid);
494         }
495     }
496
497     /*************************************************************************
498      * Create an AuthorityItem - this is a sub-resource of Authority
499      * @param specifier either a CSID or one of the urn forms
500      * @return Authority item response
501      *************************************************************************/
502     @POST
503     @Path("{csid}/items")
504     public Response createAuthorityItem(@Context ResourceMap resourceMap, @Context UriInfo ui, 
505                 @PathParam("csid") String specifier, String xmlPayload) {
506         try {
507             PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
508             ServiceContext ctx = createServiceContext(getItemServiceName(), input);
509             ctx.setResourceMap(resourceMap);
510             ctx.setUriInfo(ui);    //Laramie
511
512             // Note: must have the parentShortId, to do the create.
513             CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(specifier, "createAuthorityItem", "CREATE_ITEM", null);
514             DocumentHandler handler = createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
515             String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
516             UriBuilder path = UriBuilder.fromResource(resourceClass);
517             path.path(parent.CSID + "/items/" + itemcsid);
518             Response response = Response.created(path.build()).build();
519             return response;
520         } catch (Exception e) {
521             throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
522         }
523     }
524
525     @GET
526     @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
527     public byte[] getItemWorkflow(
528             @PathParam("csid") String csid,
529             @PathParam("itemcsid") String itemcsid) {
530         PoxPayloadOut result = null;
531
532         try {
533             ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
534             String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
535
536             MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
537             WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
538             ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
539             getRepositoryClient(ctx).get(ctx, itemcsid, handler);
540             result = ctx.getOutput();
541         } catch (Exception e) {
542             throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
543         }
544         return result.getBytes();
545     }
546
547     @PUT
548     @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
549     public byte[] updateWorkflow(
550             @PathParam("csid") String csid,
551             @PathParam("itemcsid") String itemcsid,
552             String xmlPayload) {
553         PoxPayloadOut result = null;
554         try {
555             ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
556             String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
557
558             PoxPayloadIn workflowUpdate = new PoxPayloadIn(xmlPayload);
559             MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, workflowUpdate);
560             WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
561             ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
562             getRepositoryClient(ctx).update(ctx, itemcsid, handler);
563             result = ctx.getOutput();
564         } catch (Exception e) {
565             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
566         }
567         return result.getBytes();
568     }
569
570     /**
571      * Gets the authority item.
572      * 
573      * @param parentspecifier either a CSID or one of the urn forms
574      * @param itemspecifier either a CSID or one of the urn forms
575      * 
576      * @return the authority item
577      */
578     @GET
579     @Path("{csid}/items/{itemcsid}")
580     public byte[] getAuthorityItem(
581             @Context Request request,
582             @Context UriInfo ui,
583             @PathParam("csid") String parentspecifier,
584             @PathParam("itemcsid") String itemspecifier) {
585         PoxPayloadOut result = null;
586         try {
587             JaxRsContext jaxRsContext = new JaxRsContext(request, ui);
588             MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
589             String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItem(parent)", "GET_ITEM", queryParams);
590
591             RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
592             ctx = (RemoteServiceContext) createServiceContext(getItemServiceName(), queryParams);
593             ctx.setJaxRsContext(jaxRsContext);
594
595             ctx.setUriInfo(ui); //ARG!   must pass this or subsequent calls will not have a ui.
596
597             // We omit the parentShortId, only needed when doing a create...
598             DocumentHandler handler = createItemDocumentHandler(ctx, parentcsid, null);
599
600             Specifier itemSpec = getSpecifier(itemspecifier, "getAuthorityItem(item)", "GET_ITEM");
601             if (itemSpec.form == SpecifierForm.CSID) {
602                 getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
603             } else {
604                 String itemWhereClause =
605                         buildWhereForAuthItemByName(itemSpec.value, parentcsid);
606                 DocumentFilter myFilter = new DocumentFilter(itemWhereClause, 0, 1);
607                 handler.setDocumentFilter(myFilter);
608                 getRepositoryClient(ctx).get(ctx, handler);
609             }
610             // TODO should we assert that the item is in the passed vocab?
611             result = ctx.getOutput();
612         } catch (Exception e) {
613             throw bigReThrow(e, ServiceMessages.GET_FAILED);
614         }
615         if (result == null) {
616             Response response = Response.status(Response.Status.NOT_FOUND).entity(
617                     "Get failed, the requested AuthorityItem specifier:" + itemspecifier + ": was not found.").type(
618                     "text/plain").build();
619             throw new WebApplicationException(response);
620         }
621         return result.getBytes();
622     }
623
624     /**
625      * Gets the authorityItem list for the specified authority
626      * If partialPerm is specified, keywords will be ignored.
627      * 
628      * @param specifier either a CSID or one of the urn forms
629      * @param partialTerm if non-null, matches partial terms
630      * @param keywords if non-null, matches terms in the keyword index for items
631      * @param ui passed to include additional parameters, like pagination controls
632      * 
633      * @return the authorityItem list
634      */
635     @GET
636     @Path("{csid}/items")
637     @Produces("application/xml")
638     public AbstractCommonList getAuthorityItemList(@PathParam("csid") String specifier,
639             @Context UriInfo ui) {
640         try {
641             MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
642             String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
643             String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
644             String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
645             String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
646
647             String qualifiedDisplayNameField = authorityItemCommonSchemaName + ":"
648                     + AuthorityItemJAXBSchema.DISPLAY_NAME;
649
650             // Note that docType defaults to the ServiceName, so we're fine with that.
651             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
652
653             String parentcsid = lookupParentCSID(specifier, "getAuthorityItemList", "LIST", queryParams);
654
655             ctx = createServiceContext(getItemServiceName(), queryParams);
656             // We omit the parentShortId, only needed when doing a create...
657             DocumentHandler handler = createItemDocumentHandler(ctx,
658                     parentcsid, null);
659             DocumentFilter myFilter = handler.getDocumentFilter();
660             // Need to make the default sort order for authority items
661             // be on the displayName field
662             String sortBy = queryParams.getFirst(IClientQueryParams.SORT_BY_PARAM);
663             if (sortBy == null || sortBy.isEmpty()) {
664                 myFilter.setOrderByClause(qualifiedDisplayNameField);
665             }
666             
667             myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
668                     + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
669                     + "'" + parentcsid + "'",
670                     IQueryManager.SEARCH_QUALIFIER_AND);
671
672             if (Tools.notBlank(termStatus)) {
673                 // Start with the qualified termStatus field
674                 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
675                         + AuthorityItemJAXBSchema.TERM_STATUS;
676                 String[] filterTerms = termStatus.trim().split("\\|");
677                 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
678                 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
679             }
680                 
681             // AND vocabularyitems_common:displayName LIKE '%partialTerm%'
682             // NOTE: Partial terms searches are mutually exclusive to keyword and advanced-search, but
683             // the PT query param trumps the KW and AS query params.
684             if (partialTerm != null && !partialTerm.isEmpty()) {
685                 String ptClause = QueryManager.createWhereClauseForPartialMatch(
686                         qualifiedDisplayNameField, partialTerm);
687                 myFilter.appendWhereClause(ptClause, IQueryManager.SEARCH_QUALIFIER_AND);
688             } else if (keywords != null || advancedSearch != null) {
689 //                              String kwdClause = QueryManager.createWhereClauseFromKeywords(keywords);
690 //                              myFilter.appendWhereClause(kwdClause, IQueryManager.SEARCH_QUALIFIER_AND);
691                 return search(ctx, handler, queryParams, keywords, advancedSearch);
692             }
693             if (logger.isDebugEnabled()) {
694                 logger.debug("getAuthorityItemList filtered WHERE clause: "
695                         + myFilter.getWhereClause());
696             }
697             getRepositoryClient(ctx).getFiltered(ctx, handler);
698             return (AbstractCommonList) handler.getCommonPartList();
699         } catch (Exception e) {
700             throw bigReThrow(e, ServiceMessages.LIST_FAILED);
701         }
702     }
703
704     /**
705      * @return the name of the property used to specify references for items in this type of
706      * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
707      * Some types (like Vocabulary) use a separate property.
708      */
709     protected String getRefPropName() {
710         return ServiceBindingUtils.AUTH_REF_PROP;
711     }
712     
713     /**
714      * Gets the entities referencing this Authority item instance. The service type
715      * can be passed as a query param "type", and must match a configured type
716      * for the service bindings. If not set, the type defaults to
717      * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
718      *
719      * @param parentspecifier either a CSID or one of the urn forms
720      * @param itemspecifier either a CSID or one of the urn forms
721      * @param ui the ui
722      * 
723      * @return the info for the referencing objects
724      */
725     @GET
726     @Path("{csid}/items/{itemcsid}/refObjs")
727     @Produces("application/xml")
728     public AuthorityRefDocList getReferencingObjects(
729             @PathParam("csid") String parentspecifier,
730             @PathParam("itemcsid") String itemspecifier,
731             @Context UriInfo ui) {
732         AuthorityRefDocList authRefDocList = null;
733         try {
734             MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
735
736             String parentcsid = lookupParentCSID(parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", queryParams);
737
738             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), queryParams);
739             String itemcsid = lookupItemCSID(itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS", ctx);
740
741             // Note that we have to create the service context for the Items, not the main service
742             // We omit the parentShortId, only needed when doing a create...
743             DocumentHandler handler = createItemDocumentHandler(ctx, parentcsid, null);
744             RepositoryClient repoClient = getRepositoryClient(ctx);
745             DocumentFilter myFilter = handler.getDocumentFilter();
746             String serviceType = ServiceBindingUtils.SERVICE_TYPE_PROCEDURE;
747             List<String> list = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
748             if (list != null) {
749                 serviceType = list.get(0);
750             }
751             DocumentWrapper<DocumentModel> docWrapper = repoClient.getDoc(ctx, itemcsid);
752             DocumentModel docModel = docWrapper.getWrappedObject();
753             String refName = (String) docModel.getPropertyValue(AuthorityItemJAXBSchema.REF_NAME);
754
755             // Could be smarter about using the list from above, and/or allowing multiple
756             ArrayList<String> serviceTypes = new ArrayList<String>(1);
757             serviceTypes.add(serviceType);
758             
759             authRefDocList = RefNameServiceUtils.getAuthorityRefDocs(ctx,
760                     repoClient,
761                     serviceTypes,
762                     refName,
763                     getRefPropName(),
764                     myFilter.getPageSize(), myFilter.getStartPage(), true /*computeTotal*/);
765         } catch (Exception e) {
766             throw bigReThrow(e, ServiceMessages.GET_FAILED);
767         }
768         if (authRefDocList == null) {
769             Response response = Response.status(Response.Status.NOT_FOUND).entity(
770                     "Get failed, the requested Item CSID:" + itemspecifier + ": was not found.").type(
771                     "text/plain").build();
772             throw new WebApplicationException(response);
773         }
774         return authRefDocList;
775     }
776
777     /**
778      * Gets the authority terms used in the indicated Authority item.
779      *
780      * @param parentspecifier either a CSID or one of the urn forms
781      * @param itemspecifier either a CSID or one of the urn forms
782      * @param ui passed to include additional parameters, like pagination controls
783      *
784      * @return the authority refs for the Authority item.
785      */
786     @GET
787     @Path("{csid}/items/{itemcsid}/authorityrefs")
788     @Produces("application/xml")
789     public AuthorityRefList getAuthorityItemAuthorityRefs(
790             @PathParam("csid") String parentspecifier,
791             @PathParam("itemcsid") String itemspecifier,
792             @Context UriInfo ui) {
793         AuthorityRefList authRefList = null;
794         try {
795             // Note that we have to create the service context for the Items, not the main service
796             MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
797             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
798
799             String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", queryParams);
800
801             ctx = createServiceContext(getItemServiceName(), queryParams);
802             // We omit the parentShortId, only needed when doing a create...
803             RemoteDocumentModelHandlerImpl handler =
804                     (RemoteDocumentModelHandlerImpl) createItemDocumentHandler(ctx, parentcsid, null);
805
806             String itemcsid = lookupItemCSID(itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS", ctx);
807
808             DocumentWrapper<DocumentModel> docWrapper = getRepositoryClient(ctx).getDoc(ctx, itemcsid);
809             List<String> authRefFields =
810                     ((MultipartServiceContextImpl) ctx).getCommonPartPropertyValues(
811                     ServiceBindingUtils.AUTH_REF_PROP, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
812             authRefList = handler.getAuthorityRefs(docWrapper, authRefFields);
813         } catch (Exception e) {
814             throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
815         }
816         return authRefList;
817     }
818
819     /**
820      * Update authorityItem.
821      * 
822      * @param parentspecifier either a CSID or one of the urn forms
823      * @param itemspecifier either a CSID or one of the urn forms
824      *
825      * @return the multipart output
826      */
827     @PUT
828     @Path("{csid}/items/{itemcsid}")
829     public byte[] updateAuthorityItem(
830                 @Context ResourceMap resourceMap, 
831             @Context UriInfo ui,
832             @PathParam("csid") String parentspecifier,
833             @PathParam("itemcsid") String itemspecifier,
834             String xmlPayload) {
835         PoxPayloadOut result = null;
836         try {
837             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
838             // Note that we have to create the service context for the Items, not the main service
839             //Laramie CSPACE-3175.  passing null for queryParams, because prior to this refactor, the code moved to lookupParentCSID in this instance called the version of getServiceContext() that passes null
840             String parentcsid = lookupParentCSID(parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
841
842             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), theUpdate);
843             ctx.setResourceMap(resourceMap);
844             String itemcsid = lookupItemCSID(itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM", ctx);
845
846             // We omit the parentShortId, only needed when doing a create...
847             DocumentHandler handler = createItemDocumentHandler(ctx, parentcsid, null);
848             ctx.setUriInfo(ui);
849             getRepositoryClient(ctx).update(ctx, itemcsid, handler);
850             result = ctx.getOutput();
851
852         } catch (Exception e) {
853             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
854         }
855         return result.getBytes();
856     }
857
858     /**
859      * Delete authorityItem.
860      * 
861      * @param parentcsid the parentcsid
862      * @param itemcsid the itemcsid
863      * 
864      * @return the response
865      */
866     @DELETE
867     @Path("{csid}/items/{itemcsid}")
868     public Response deleteAuthorityItem(
869             @PathParam("csid") String parentcsid,
870             @PathParam("itemcsid") String itemcsid) {
871         //try{
872         if (logger.isDebugEnabled()) {
873             logger.debug("deleteAuthorityItem with parentcsid=" + parentcsid + " and itemcsid=" + itemcsid);
874         }
875         try {
876             ensureCSID(parentcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
877             ensureCSID(itemcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
878             //Laramie, removing this catch, since it will surely fail below, since itemcsid or parentcsid will be null.
879             // }catch (Throwable t){
880             //    System.out.println("ERROR in setting up DELETE: "+t);
881             // }
882             // try {
883             // Note that we have to create the service context for the Items, not the main service
884             ServiceContext ctx = createServiceContext(getItemServiceName());
885             getRepositoryClient(ctx).delete(ctx, itemcsid);
886             return Response.status(HttpResponseCodes.SC_OK).build();
887         } catch (Exception e) {
888             throw bigReThrow(e, ServiceMessages.DELETE_FAILED + "  itemcsid: " + itemcsid + " parentcsid:" + parentcsid);
889         }
890     }
891     public final static String hierarchy = "hierarchy";
892
893     @GET
894     @Path("{csid}/items/{itemcsid}/" + hierarchy)
895     @Produces("application/xml")
896     public String getHierarchy(@PathParam("csid") String csid,
897             @PathParam("itemcsid") String itemcsid,
898             @Context UriInfo ui) throws Exception {
899         try {
900             // 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...?
901             String calledUri = ui.getPath();
902             String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
903             ServiceContext ctx = createServiceContext(getItemServiceName());
904             ctx.setUriInfo(ui);
905             String direction = ui.getQueryParameters().getFirst(Hierarchy.directionQP);
906             if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
907                 return Hierarchy.surface(ctx, itemcsid, uri);
908             } else {
909                 return Hierarchy.dive(ctx, itemcsid, uri);
910             }
911         } catch (Exception e) {
912             throw bigReThrow(e, "Error showing hierarchy", itemcsid);
913         }
914     }
915 }