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