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:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
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.
24 package org.collectionspace.services.common.vocabulary;
26 import java.util.List;
29 import javax.ws.rs.Consumes;
30 import javax.ws.rs.DELETE;
31 import javax.ws.rs.GET;
32 import javax.ws.rs.POST;
33 import javax.ws.rs.PUT;
34 import javax.ws.rs.Path;
35 import javax.ws.rs.PathParam;
36 import javax.ws.rs.Produces;
37 import javax.ws.rs.core.Context;
38 import javax.ws.rs.core.MultivaluedMap;
39 import javax.ws.rs.core.Request;
40 import javax.ws.rs.core.Response;
41 import javax.ws.rs.core.UriBuilder;
42 import javax.ws.rs.core.UriInfo;
43 import javax.ws.rs.core.Response.ResponseBuilder;
45 import org.collectionspace.services.client.IClientQueryParams;
46 import org.collectionspace.services.client.IQueryManager;
47 import org.collectionspace.services.client.PoxPayload;
48 import org.collectionspace.services.client.PoxPayloadIn;
49 import org.collectionspace.services.client.PoxPayloadOut;
50 import org.collectionspace.services.client.XmlTools;
51 import org.collectionspace.services.client.workflow.WorkflowClient;
53 import org.collectionspace.services.common.CSWebApplicationException;
54 import org.collectionspace.services.common.NuxeoBasedResource;
55 import org.collectionspace.services.common.ResourceMap;
56 import org.collectionspace.services.common.ServiceMain;
57 import org.collectionspace.services.common.ServiceMessages;
58 import org.collectionspace.services.common.StoredValuesUriTemplate;
59 import org.collectionspace.services.common.UriInfoWrapper;
60 import org.collectionspace.services.common.UriTemplateFactory;
61 import org.collectionspace.services.common.UriTemplateRegistryKey;
62 import org.collectionspace.services.common.api.RefName;
63 import org.collectionspace.services.common.api.RefNameUtils;
64 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
65 import org.collectionspace.services.common.api.Tools;
66 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
67 import org.collectionspace.services.common.authorityref.AuthorityRefList;
68 import org.collectionspace.services.common.context.JaxRsContext;
69 import org.collectionspace.services.common.context.MultipartServiceContext;
70 import org.collectionspace.services.common.context.RemoteServiceContext;
71 import org.collectionspace.services.common.context.ServiceBindingUtils;
72 import org.collectionspace.services.common.context.ServiceContext;
73 import org.collectionspace.services.common.document.DocumentException;
74 import org.collectionspace.services.common.document.DocumentFilter;
75 import org.collectionspace.services.common.document.DocumentHandler;
76 import org.collectionspace.services.common.document.DocumentNotFoundException;
77 import org.collectionspace.services.common.document.DocumentReferenceException;
78 import org.collectionspace.services.common.document.DocumentWrapper;
79 import org.collectionspace.services.common.document.Hierarchy;
80 import org.collectionspace.services.common.query.QueryManager;
81 import org.collectionspace.services.common.repository.RepositoryClient;
82 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
83 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
84 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
85 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
86 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
88 import org.collectionspace.services.config.ClientType;
89 import org.collectionspace.services.config.service.ServiceBindingType;
90 import org.collectionspace.services.jaxb.AbstractCommonList;
91 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
92 import org.collectionspace.services.lifecycle.TransitionDef;
93 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
94 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
95 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentFilter;
96 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
97 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
98 import org.collectionspace.services.workflow.WorkflowCommon;
99 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
100 import org.collectionspace.services.description.ServiceDescription;
102 import org.jboss.resteasy.util.HttpResponseCodes;
103 import org.nuxeo.ecm.core.api.DocumentModel;
104 import org.nuxeo.ecm.core.api.DocumentModelList;
105 import org.slf4j.Logger;
106 import org.slf4j.LoggerFactory;
107 import org.w3c.dom.Element;
110 * The Class AuthorityResource.
113 @SuppressWarnings({"rawtypes", "unchecked"})
114 @Consumes("application/xml")
115 @Produces("application/xml")
116 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
117 extends NuxeoBasedResource {
119 final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
121 final static String SEARCH_TYPE_TERMSTATUS = "ts";
122 public final static String hierarchy = "hierarchy";
124 private static final Integer PAGE_NUM_FROM_QUERYPARAMS = null;
125 private static final Integer PAGE_SIZE_FROM_QUERYPARAMS = null;
127 protected Class<AuthCommon> authCommonClass;
128 protected Class<?> resourceClass;
129 protected String authorityCommonSchemaName;
130 protected String authorityItemCommonSchemaName;
131 final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed? I see no references to it.
133 final static String FETCH_SHORT_ID = "_fetch_";
134 public final static String PARENT_WILDCARD = "_ALL_";
135 protected static final boolean DONT_INCLUDE_ITEMS = false;
136 protected static final boolean INCLUDE_ITEMS = true;
139 * Instantiates a new Authority resource.
141 public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
142 String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
143 this.authCommonClass = authCommonClass;
144 this.resourceClass = resourceClass;
145 this.authorityCommonSchemaName = authorityCommonSchemaName;
146 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
149 public abstract String getItemServiceName();
151 public abstract String getItemTermInfoGroupXPathBase();
154 protected String getVersionString() {
155 return "$LastChangedRevision: 2617 $";
159 public Class<AuthCommon> getCommonPartClass() {
160 return authCommonClass;
164 * Creates the item document handler.
167 * @param inAuthority the in vocabulary
169 * @return the document handler
171 * @throws Exception the exception
173 protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
174 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
175 String inAuthority, String containerShortIdentifier)
177 String authorityRefNameBase;
178 AuthorityItemDocumentModelHandler<?> docHandler;
180 if (containerShortIdentifier == null) {
181 authorityRefNameBase = null;
183 ServiceContext<PoxPayloadIn, PoxPayloadOut> containerCtx = createServiceContext(getServiceName());
184 if (containerShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
185 if (ctx.getCurrentRepositorySession() != null) {
186 containerCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
188 // Get from parent document
189 containerShortIdentifier = getAuthShortIdentifier(containerCtx, inAuthority);
191 authorityRefNameBase = buildAuthorityRefNameBase(containerCtx, containerShortIdentifier);
194 docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
195 ctx.getCommonPartLabel(getItemServiceName()),
197 // FIXME - Richard and Aron think the following three lines should
198 // be in the constructor for the AuthorityItemDocumentModelHandler
199 // because all three are required fields.
200 docHandler.setInAuthority(inAuthority);
201 docHandler.setAuthorityRefNameBase(authorityRefNameBase);
202 docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
206 public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
207 throws DocumentNotFoundException, DocumentException {
208 String shortIdentifier = null;
211 AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
212 shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
213 } catch (Exception e) {
214 if (logger.isDebugEnabled()) {
215 logger.debug("Caught exception ", e);
217 throw new DocumentException(e);
220 return shortIdentifier;
223 protected String buildAuthorityRefNameBase(
224 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
225 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
226 ctx.getServiceName(),
227 null, // Only use shortId form!!!
228 shortIdentifier, null);
229 return authority.toString();
232 public static class CsidAndShortIdentifier {
234 String shortIdentifier;
237 protected String lookupParentCSID(String parentspecifier, String method,
238 String op, UriInfo uriInfo) throws Exception {
239 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(NULL_CONTEXT,
240 parentspecifier, method, op, uriInfo);
241 return tempResult.CSID;
244 protected String lookupParentCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentspecifier, String method,
245 String op, UriInfo uriInfo) throws Exception {
246 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
247 parentspecifier, method, op, uriInfo);
248 return tempResult.CSID;
252 private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
253 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx, // Ok to be null
254 String parentIdentifier,
259 CsidAndShortIdentifier result = new CsidAndShortIdentifier();
260 Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
263 String parentShortIdentifier;
264 if (parentSpec.form == SpecifierForm.CSID) {
265 parentShortIdentifier = null;
266 parentcsid = parentSpec.value;
267 // Uncomment when app layer is ready to integrate
268 // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
269 parentShortIdentifier = FETCH_SHORT_ID;
271 parentShortIdentifier = parentSpec.value;
272 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
273 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
274 CoreSessionInterface repoSession = null;
275 if (existingCtx != null) {
276 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession(); // We want to use the thread's current repo session
278 parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
281 result.CSID = parentcsid;
282 result.shortIdentifier = parentShortIdentifier;
287 public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemSpecifier, String parentCsid, String method, String op)
290 Specifier itemSpec = Specifier.getSpecifier(itemSpecifier, method, op);
292 if (itemSpec.form == SpecifierForm.CSID) {
293 itemCsid = itemSpec.value;
295 CoreSessionInterface repoSession = null;
296 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
298 if (existingContext != null) {
299 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession(); // We want to use the thread's current repo session
302 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentCsid);
303 itemCsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
310 * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then
311 * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
312 * Resource. They then call this method on that resource.
315 public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item)
316 throws Exception, DocumentNotFoundException {
320 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
321 // Ensure we have the right context.
322 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
324 // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
325 NuxeoRepositoryClientImpl client = (NuxeoRepositoryClientImpl)getRepositoryClient(ctx);
326 String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
328 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
329 ctx = createServiceContext(getItemServiceName());
330 DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
331 DocumentModel docModel = docWrapper.getWrappedObject();
337 public Response createAuthority(
338 @Context ResourceMap resourceMap,
339 @Context UriInfo uriInfo,
342 // Requests to create new authorities come in on new threads. Unfortunately, we need to synchronize those threads on this block because, as of 8/27/2015, we can't seem to get Nuxeo
343 // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
344 // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
345 // the code that creates new authorities. The authority document model handler will first check for authorities with the same short id before
346 // trying to create a new authority.
348 synchronized(AuthorityResource.class) {
350 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
351 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
352 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
354 String csid = getRepositoryClient(ctx).create(ctx, handler);
355 UriBuilder path = UriBuilder.fromResource(resourceClass);
356 path.path("" + csid);
357 Response response = Response.created(path.build()).build();
359 } catch (Exception e) {
360 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
365 protected boolean supportsReplicating(String tenantId, String serviceName) {
366 boolean result = false;
368 ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
369 result = sb.isSupportsReplicating();
375 * Synchronizes the authority and its items/terms with a Shared Authority Server.
377 * @param specifier either a CSID or one of the urn forms
379 * @return the authority
383 public byte[] synchronize(
384 @Context Request request,
385 @Context UriInfo uriInfo,
386 @PathParam("csid") String identifier) {
387 uriInfo = new UriInfoWrapper(uriInfo);
389 boolean neededSync = false;
390 PoxPayloadOut payloadOut = null;
394 // Prevent multiple SAS synchronizations from occurring simultaneously by synchronizing this method.
396 synchronized(AuthorityResource.class) {
398 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
400 * Make sure this authority service supports synchronization
402 if (supportsReplicating(ctx.getTenantId(), ctx.getServiceName()) == false) {
403 throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
405 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
406 specifier = Specifier.getSpecifier(identifier, "getAuthority", "GET");
407 handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
408 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
409 payloadOut = ctx.getOutput();
410 } catch (Exception e) {
411 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, identifier);
415 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
417 if (neededSync == true) {
418 result = payloadOut.getBytes();
420 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
421 specifier.value).getBytes();
422 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
423 throw new CSWebApplicationException(response);
431 * Builds a cached JAX-RS response.
433 protected Response buildResponse(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, PoxPayloadOut payloadOut) {
434 Response result = null;
436 ResponseBuilder responseBuilder = Response.ok(payloadOut.getBytes());
437 this.setCacheControl(ctx, responseBuilder);
438 result = responseBuilder.build();
444 * Gets the authority.
446 * @param specifier either a CSID or one of the urn forms
448 * @return the authority
454 @Context Request request,
455 @Context ResourceMap resourceMap,
456 @Context UriInfo uriInfo,
457 @PathParam("csid") String specifier) {
458 Response result = null;
459 uriInfo = new UriInfoWrapper(uriInfo);
460 PoxPayloadOut payloadout = null;
464 // If the specifier is a fully qualified authority term refname, then return the term payload in the response
466 if (RefNameUtils.isTermRefname(specifier)) {
467 AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(specifier);
468 String parentIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name);
469 String itemIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.name);
470 result = this.getAuthorityItemResponse(request, uriInfo, resourceMap, parentIdentifier, itemIdentifier);
472 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(request, uriInfo);
473 payloadout = getAuthority(ctx, request, uriInfo, specifier, DONT_INCLUDE_ITEMS);
474 result = buildResponse(ctx, payloadout);
476 } catch (Exception e) {
477 throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
480 if (result == null) {
481 Response response = Response.status(Response.Status.NOT_FOUND).entity(
482 "GET request failed. The requested Authority specifier:" + specifier + ": was not found.").type(
483 "text/plain").build();
484 throw new CSWebApplicationException(response);
490 protected PoxPayloadOut getAuthority(
491 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
495 boolean includeItems) throws Exception {
496 uriInfo = new UriInfoWrapper(uriInfo);
497 PoxPayloadOut payloadout = null;
499 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> docHandler = createDocumentHandler(ctx);
500 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
501 if (spec.form == SpecifierForm.CSID) {
502 if (logger.isDebugEnabled()) {
503 logger.debug("getAuthority with csid=" + spec.value);
505 getRepositoryClient(ctx).get(ctx, spec.value, docHandler);
507 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
508 DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
509 docHandler.setDocumentFilter(myFilter);
510 getRepositoryClient(ctx).get(ctx, docHandler);
513 payloadout = ctx.getOutput();
514 if (includeItems == true) {
515 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
516 payloadout.addPart(PoxPayload.ABSTRACT_COMMON_LIST_ROOT_ELEMENT_LABEL, itemsList);
523 * Finds and populates the authority list.
527 * @return the authority list
530 @Produces("application/xml")
531 public AbstractCommonList getAuthorityList(@Context UriInfo uriInfo) { //FIXME - REM 5/3/2012 - This is not reachable from the JAX-RS dispatcher. Instead the equivalent method in ResourceBase is getting called.
532 uriInfo = new UriInfoWrapper(uriInfo);
533 AbstractCommonList result = null;
536 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
537 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
539 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
540 DocumentFilter myFilter = handler.getDocumentFilter();
541 // Need to make the default sort order for authority items
542 // be on the displayName field
543 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
544 if (orderBy == null || orderBy.isEmpty()) {
545 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
546 + AuthorityItemJAXBSchema.DISPLAY_NAME;
547 myFilter.setOrderByClause(qualifiedDisplayNameField);
549 String nameQ = queryParams.getFirst("refName");
551 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
553 //getRepositoryClient(ctx).getFiltered(ctx, handler); # Something here?
554 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
555 result = search(ctx, handler, uriInfo, orderBy, null, advancedSearch, null);
556 result = handler.getCommonPartList();
557 } catch (Exception e) {
558 throw bigReThrow(e, ServiceMessages.GET_FAILED);
565 * Overriding this methods to see if we should update the revision number during the update. We don't
566 * want to update the rev number of synchronization operations.
569 protected PoxPayloadOut update(String csid,
570 PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
571 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
573 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
574 Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
575 if (shouldUpdateRev != null) {
576 handler.setShouldUpdateRevNumber(shouldUpdateRev);
578 getRepositoryClient(ctx).update(ctx, csid, handler);
579 return ctx.getOutput();
585 * @param specifier the csid or id
587 * @return the multipart output
591 public byte[] updateAuthority(
592 @Context Request request,
593 @Context ResourceMap resourceMap,
594 @Context UriInfo uriInfo,
595 @PathParam("csid") String specifier,
597 PoxPayloadOut result = null;
599 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
600 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
601 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
602 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
603 String csid = getCsid(ctx, spec);
604 getRepositoryClient(ctx).update(ctx, csid, handler);
605 result = ctx.getOutput();
606 } catch (Exception e) {
607 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
609 return result.getBytes();
613 * Delete all the items in an authority list.
620 @Path("{csid}/items")
621 public Response deleteAuthorityItemList(@PathParam("csid") String specifier,
622 @Context UriInfo uriInfo) {
623 uriInfo = new UriInfoWrapper(uriInfo);
626 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
627 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
629 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
631 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
633 // Delete all the items one by one
635 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
636 for (ListItem item : itemsList.getListItem()) {
637 deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
639 } catch (Throwable t) {
640 repoSession.setTransactionRollbackOnly();
643 repoClient.releaseRepositorySession(ctx, repoSession);
646 return Response.status(HttpResponseCodes.SC_OK).build();
647 } catch (Exception e) {
648 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
655 * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
657 * @return the response
661 public Response deleteAuthority( // # Delete this authority and all of it's items.
662 @Context Request request,
663 @Context UriInfo uriInfo,
664 @PathParam("csid") String specifier) {
665 uriInfo = new UriInfoWrapper(uriInfo);
667 if (logger.isDebugEnabled()) {
668 logger.debug("deleteAuthority with specifier=" + specifier);
672 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
673 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
674 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
676 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
678 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
680 // First try to delete all the items
682 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
683 for (ListItem item : itemsList.getListItem()) {
684 deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
688 // Lastly, delete the parent/container
690 if (spec.form == SpecifierForm.CSID) {
691 if (logger.isDebugEnabled()) {
692 logger.debug("deleteAuthority with csid=" + spec.value);
694 ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
695 getRepositoryClient(ctx).delete(ctx, spec.value, handler);
697 if (logger.isDebugEnabled()) {
698 logger.debug("deleteAuthority with specifier=" + spec.value);
700 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
701 getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
703 } catch (Throwable t) {
704 repoSession.setTransactionRollbackOnly();
707 repoClient.releaseRepositorySession(ctx, repoSession);
710 return Response.status(HttpResponseCodes.SC_OK).build();
711 } catch (Exception e) {
712 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
716 protected String getCsid(ListItem item) {
717 String result = null;
719 for (Element ele : item.getAny()) {
720 String elementName = ele.getTagName().toLowerCase();
721 if (elementName.equals("csid")) {
722 result = ele.getTextContent();
733 * @param parentspecifier - ID of the container. Can be URN or CSID form
734 * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
735 * @param isProposed - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
739 protected Response createAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentIdentifier,
740 boolean shouldUpdateRevNumber,
742 boolean isSasItem) throws Exception {
743 Response result = null;
745 // Note: must have the parentShortId, to do the create.
746 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
747 AuthorityItemDocumentModelHandler handler =
748 (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
749 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
750 handler.setIsProposed(isProposed);
751 handler.setIsSASItem(isSasItem);
752 // Make the client call
753 String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
755 // Build the JAX-RS response
756 UriBuilder path = UriBuilder.fromResource(resourceClass);
757 path.path(parent.CSID + "/items/" + itemcsid);
758 result = Response.created(path.build()).build();
763 public PoxPayloadOut updateAuthorityItem(
764 ServiceContext<PoxPayloadIn, PoxPayloadOut> itemServiceCtx, // Ok to be null. Will be null on PUT calls, but not on sync calls
765 ResourceMap resourceMap,
767 String parentspecifier,
768 String itemspecifier,
769 PoxPayloadIn theUpdate,
770 boolean shouldUpdateRevNumber,
774 PoxPayloadOut result = null;
776 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
777 String parentcsid = csidAndShortId.CSID;
778 String parentShortId = csidAndShortId.shortIdentifier;
780 // If the itemServiceCtx context is not null, use it. Otherwise, create a new context
782 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
784 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
786 ctx.setInput(theUpdate); // the update payload
789 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
791 // We omit the parentShortId, only needed when doing a create...
792 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
793 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
795 // Update the SAS fields if either value is non-null
797 boolean updateSASFields = isProposed != null || isSASItem != null;
798 handler.setshouldUpdateSASFields(updateSASFields);
799 if (updateSASFields == true) {
800 handler.setshouldUpdateSASFields(true);
801 if (isProposed != null) {
802 handler.setIsProposed(isProposed);
804 if (isSASItem != null) {
805 handler.setIsSASItem(isSASItem);
809 getRepositoryClient(ctx).update(ctx, itemcsid, handler);
810 result = ctx.getOutput();
816 * Called with an existing context.
818 * @param parentIdentifier
823 public Response createAuthorityItemWithParentContext(ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx,
824 String parentIdentifier,
826 boolean shouldUpdateRevNumber,
828 boolean isSASItem) throws Exception {
829 Response result = null;
831 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
832 parentCtx.getResourceMap(), parentCtx.getUriInfo());
833 if (parentCtx.getCurrentRepositorySession() != null) {
834 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
836 result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
841 /*************************************************************************
842 * Create an AuthorityItem - this is a sub-resource of Authority
843 * @param specifier either a CSID or one of the urn forms
844 * @return Authority item response
845 *************************************************************************/
847 @Path("{csid}/items")
848 public Response createAuthorityItem(
849 @Context ResourceMap resourceMap,
850 @Context UriInfo uriInfo,
851 @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
853 uriInfo = new UriInfoWrapper(uriInfo);
854 Response result = null;
857 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
858 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
859 result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
860 AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
861 } catch (Exception e) {
862 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
869 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
870 public byte[] getItemWorkflow(
871 @PathParam("csid") String csid,
872 @PathParam("itemcsid") String itemcsid) {
873 PoxPayloadOut result = null;
876 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
877 String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
879 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
880 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
881 ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
882 getRepositoryClient(ctx).get(ctx, itemcsid, handler);
883 result = ctx.getOutput();
884 } catch (Exception e) {
885 throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
887 return result.getBytes();
891 * We should consider changing this code. The RepositoryClient (from call to getRepositoryClient) could support a call doWorkflowTransition() instead?
895 @Path("{csid}" + WorkflowClient.SERVICE_PATH + "/" + "{transition}")
896 public byte[] updateWorkflowWithTransition(
897 @Context UriInfo uriInfo,
898 @PathParam("csid") String specifier,
899 @PathParam("transition") String transition) {
900 PoxPayloadOut result = null;
902 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
905 csid = getCsid(null, spec);
906 result = updateWorkflowWithTransition(NULL_CONTEXT, uriInfo, csid, transition);
907 } catch (Exception e) {
908 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
911 return result.getBytes();
914 //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
915 // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
917 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
918 public byte[] updateItemWorkflowWithTransition(
919 @Context UriInfo uriInfo,
920 @PathParam("csid") String parentIdentifier,
921 @PathParam("itemcsid") String itemIdentifier,
922 @PathParam("transition") String transition) {
923 uriInfo = new UriInfoWrapper(uriInfo);
924 PoxPayloadOut result = null;
927 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
928 result = updateItemWorkflowWithTransition(ctx,
929 parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
930 } catch (Exception e) {
931 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
934 return result.getBytes();
938 * Update an authority item's workflow state.
939 * @param existingContext
944 * @throws DocumentReferenceException
946 public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
947 String parentIdentifier,
948 String itemIdentifier,
950 boolean updateRevNumber) throws DocumentReferenceException {
951 PoxPayloadOut result = null;
955 // We need CSIDs for both the parent authority and the authority item
957 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
958 String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
961 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
963 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(),
964 WorkflowClient.SERVICE_COMMONPART_NAME);
965 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
966 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
967 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
970 // Create a service context and document handler for the target resource -not the workflow resource itself.
972 ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
973 AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
974 targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
975 ctx.setProperty(WorkflowClient.TARGET_DOCHANDLER, targetDocHandler); //added as a context param for the workflow document handler -it will call the parent's dochandler "prepareForWorkflowTranstion" method
977 // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
979 String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
980 ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
982 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
983 TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
984 if (transitionDef == null) {
985 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
986 itemIdentifier, transition));
988 ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
990 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
991 getRepositoryClient(ctx).update(ctx, itemCsid, handler);
992 result = ctx.getOutput();
993 } catch (DocumentReferenceException de) {
995 } catch (Exception e) {
996 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
1002 protected PoxPayloadOut getAuthorityItem(
1003 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1004 String parentIdentifier,
1005 String itemIdentifier) throws Exception {
1006 PoxPayloadOut result = null;
1008 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
1009 // We omit the parentShortId, only needed when doing a create...
1010 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
1012 Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
1013 if (itemSpec.form == SpecifierForm.CSID) {
1014 // TODO should we assert that the item is in the passed vocab?
1015 getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
1017 String itemWhereClause =
1018 RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
1019 DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
1020 handler.setDocumentFilter(myFilter);
1021 getRepositoryClient(ctx).get(ctx, handler);
1024 result = (PoxPayloadOut) ctx.getOutput();
1025 if (result != null) {
1026 String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
1027 if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
1028 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
1029 itemSpec.value, inAuthority, parentcsid));
1036 public PoxPayloadOut getAuthorityItemWithExistingContext(
1037 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1038 String parentIdentifier,
1039 String itemIdentifier) throws Exception {
1040 PoxPayloadOut result = null;
1042 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
1043 if (existingCtx.getCurrentRepositorySession() != null) {
1044 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
1045 ctx.setProperties(existingCtx.getProperties());
1047 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1053 * Gets the authority item.
1055 * @param parentspecifier either a CSID or one of the urn forms
1056 * @param itemspecifier either a CSID or one of the urn forms
1058 * @return the authority item
1061 @Path("{csid}/items/{itemcsid}")
1062 public byte[] getAuthorityItem(
1063 @Context Request request,
1064 @Context UriInfo uriInfo,
1065 @Context ResourceMap resourceMap,
1066 @PathParam("csid") String parentIdentifier,
1067 @PathParam("itemcsid") String itemIdentifier) {
1068 uriInfo = new UriInfoWrapper(uriInfo);
1069 PoxPayloadOut result = null;
1071 result = this.getAuthorityItemPayload(request, uriInfo, resourceMap, parentIdentifier, itemIdentifier);
1073 return result.getBytes();
1077 public PoxPayloadOut getAuthorityItemPayload(
1078 @Context Request request,
1079 @Context UriInfo uriInfo,
1080 @Context ResourceMap resourceMap,
1081 @PathParam("csid") String parentIdentifier,
1082 @PathParam("itemcsid") String itemIdentifier) {
1083 uriInfo = new UriInfoWrapper(uriInfo);
1084 PoxPayloadOut result = null;
1086 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx =
1087 (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1089 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1090 ctx.setJaxRsContext(jaxRsContext);
1092 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1093 } catch (DocumentNotFoundException dnf) {
1094 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1095 } catch (Exception e) {
1096 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1102 public Response getAuthorityItemResponse(
1103 @Context Request request,
1104 @Context UriInfo uriInfo,
1105 @Context ResourceMap resourceMap,
1106 @PathParam("csid") String parentIdentifier,
1107 @PathParam("itemcsid") String itemIdentifier) {
1108 uriInfo = new UriInfoWrapper(uriInfo);
1109 PoxPayloadOut payloadout = null;
1110 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
1113 ctx = (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1115 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1116 ctx.setJaxRsContext(jaxRsContext);
1118 payloadout = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1119 } catch (DocumentNotFoundException dnf) {
1120 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1121 } catch (Exception e) {
1122 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1125 return buildResponse(ctx, payloadout);
1130 * Most of the authority child classes will/should use this implementation. However, the Vocabulary service's item schema is
1131 * different enough that it will have to override this method in it's resource class.
1134 protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1135 String result = null;
1137 result = NuxeoUtils.getPrimaryElPathPropertyName(
1138 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1139 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1145 protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1146 String result = null;
1148 result = NuxeoUtils.getMultiElPathPropertyName(
1149 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1150 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1156 * Gets the authorityItem list for the specified authority
1157 * If partialPerm is specified, keywords will be ignored.
1159 * @param authorityIdentifier either a CSID or one of the urn forms
1160 * @param partialTerm if non-null, matches partial terms
1161 * @param keywords if non-null, matches terms in the keyword index for items
1162 * @param ui passed to include additional parameters, like pagination controls
1165 public AbstractCommonList getAuthorityItemList(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1166 String authorityIdentifier,
1167 UriInfo uriInfo) throws Exception {
1168 AbstractCommonList result = null;
1170 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1171 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1172 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
1173 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
1174 ctx.setProperties(existingContext.getProperties());
1177 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
1178 String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
1179 String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
1180 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
1181 String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
1183 // For the wildcard case, parentcsid is null, but docHandler will deal with this.
1184 // We omit the parentShortId, only needed when doing a create...
1185 String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
1186 lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
1187 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
1188 createItemDocumentHandler(ctx, parentcsid, null);
1190 DocumentFilter myFilter = handler.getDocumentFilter();
1191 // If we are not wildcarding the parent, add a restriction
1192 if (parentcsid != null) {
1193 myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
1194 + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
1195 + "'" + parentcsid + "'",
1196 IQueryManager.SEARCH_QUALIFIER_AND);
1199 if (Tools.notBlank(termStatus)) {
1200 // Start with the qualified termStatus field
1201 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
1202 + AuthorityItemJAXBSchema.TERM_STATUS;
1203 String[] filterTerms = termStatus.trim().split("\\|");
1204 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
1205 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
1208 result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
1214 * Gets the authorityItem list for the specified authority
1215 * If partialPerm is specified, keywords will be ignored.
1217 * @param authorityIdentifier either a CSID or one of the urn forms
1218 * @param partialTerm if non-null, matches partial terms
1219 * @param keywords if non-null, matches terms in the keyword index for items
1220 * @param ui passed to include additional parameters, like pagination controls
1222 * @return the authorityItem list
1225 @Path("{csid}/items")
1226 @Produces("application/xml")
1227 public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
1228 @Context UriInfo uriInfo) {
1229 uriInfo = new UriInfoWrapper(uriInfo);
1230 AbstractCommonList result = null;
1233 result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);
1234 } catch (Exception e) {
1235 throw bigReThrow(e, ServiceMessages.LIST_FAILED);
1242 * @return the name of the property used to specify references for items in this type of
1243 * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
1244 * Some types (like Vocabulary) use a separate property.
1246 protected String getRefPropName() {
1247 return ServiceBindingUtils.AUTH_REF_PROP;
1251 * Gets the entities referencing this Authority item instance. The service type
1252 * can be passed as a query param "type", and must match a configured type
1253 * for the service bindings. If not set, the type defaults to
1254 * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1256 * @param parentspecifier either a CSID or one of the urn forms
1257 * @param itemspecifier either a CSID or one of the urn forms
1260 * @return the info for the referencing objects
1263 @Path("{csid}/items/{itemcsid}/refObjs")
1264 @Produces("application/xml")
1265 public AuthorityRefDocList getReferencingObjects(
1266 @PathParam("csid") String parentSpecifier,
1267 @PathParam("itemcsid") String itemSpecifier,
1268 @Context UriInfo uriInfo) {
1269 uriInfo = new UriInfoWrapper(uriInfo);
1270 AuthorityRefDocList authRefDocList = null;
1272 authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriInfo, PAGE_NUM_FROM_QUERYPARAMS, PAGE_SIZE_FROM_QUERYPARAMS, true, true);
1273 } catch (Exception e) {
1274 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1277 if (authRefDocList == null) {
1278 Response response = Response.status(Response.Status.NOT_FOUND).entity(
1279 "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1280 "text/plain").build();
1281 throw new CSWebApplicationException(response);
1283 return authRefDocList;
1286 public AuthorityRefDocList getReferencingObjects(
1287 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1288 String parentspecifier,
1289 String itemspecifier,
1293 boolean useDefaultOrderByClause,
1294 boolean computeTotal) throws Exception {
1295 AuthorityRefDocList authRefDocList = null;
1297 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1298 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1300 // Merge parts of existing context with our new context
1302 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1303 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If one exists, use the existing repo session
1304 ctx.setProperties(existingContext.getProperties());
1307 String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1308 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1310 // Remove the "type" property from the query params
1311 List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1312 if (serviceTypes == null || serviceTypes.isEmpty()) {
1313 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1316 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1317 authRefDocList = handler.getReferencingObjects(ctx, serviceTypes, getRefPropName(), itemcsid, pageNum, pageSize, useDefaultOrderByClause, computeTotal);
1319 return authRefDocList;
1323 * Gets the authority terms used in the indicated Authority item.
1325 * @param parentspecifier either a CSID or one of the urn forms
1326 * @param itemspecifier either a CSID or one of the urn forms
1327 * @param ui passed to include additional parameters, like pagination controls
1329 * @return the authority refs for the Authority item.
1332 @Path("{csid}/items/{itemcsid}/authorityrefs")
1333 @Produces("application/xml")
1334 public AuthorityRefList getAuthorityItemAuthorityRefs(
1335 @PathParam("csid") String parentspecifier,
1336 @PathParam("itemcsid") String itemspecifier,
1337 @Context UriInfo uriInfo) {
1338 uriInfo = new UriInfoWrapper(uriInfo);
1339 AuthorityRefList authRefList = null;
1342 // Note that we have to create the service context for the Items, not the main service
1343 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1344 String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1345 // We omit the parentShortId, only needed when doing a create...
1346 DocumentModelHandler<?, AbstractCommonList> handler =
1347 (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1349 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1351 List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1352 authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1353 } catch (Exception e) {
1354 throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1361 * Synchronizes a local authority item with a share authority server (SAS) item.
1363 * @param parentIdentifier
1364 * @param itemIdentifier
1368 private PoxPayloadOut synchronizeItem(
1369 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1370 String parentIdentifier,
1371 String itemIdentifier,
1372 boolean syncHierarchicalRelationships) throws Exception {
1373 PoxPayloadOut result = null;
1374 AuthorityItemSpecifier specifier;
1375 boolean neededSync = false;
1377 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1378 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1379 handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1380 handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1381 handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1382 // Create an authority item specifier
1383 Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1384 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1385 specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1387 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1388 if (neededSync == true) {
1389 result = (PoxPayloadOut) ctx.getOutput();
1396 * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1397 * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1398 * local authority. The parent context was created for the authority (parent) because the sync started there.
1399 * @param existingCtx
1400 * @param parentIdentifier
1401 * @param itemIdentifier
1405 public PoxPayloadOut synchronizeItemWithExistingContext(
1406 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1407 String parentIdentifier,
1408 String itemIdentifier,
1409 boolean syncHierarchicalRelationships
1410 ) throws Exception {
1411 PoxPayloadOut result = null;
1413 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1414 existingCtx.getResourceMap(),
1415 existingCtx.getUriInfo());
1416 if (existingCtx.getCurrentRepositorySession() != null) {
1417 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1420 result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1426 * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1428 * @param specifier either CSIDs and/or one of the urn forms
1430 * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1433 @Path("{csid}/items/{itemcsid}/sync")
1434 public byte[] synchronizeItem(
1435 @Context ResourceMap resourceMap,
1436 @Context UriInfo uriInfo,
1437 @PathParam("csid") String parentIdentifier,
1438 @PathParam("itemcsid") String itemIdentifier) {
1439 uriInfo = new UriInfoWrapper(uriInfo);
1441 boolean neededSync = false;
1442 PoxPayloadOut payloadOut = null;
1445 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1446 payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1447 if (payloadOut != null) {
1450 } catch (Exception e) {
1451 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1455 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
1457 if (neededSync == true) {
1458 result = payloadOut.getBytes();
1460 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1461 itemIdentifier).getBytes();
1462 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1463 throw new CSWebApplicationException(response);
1470 * Update authorityItem.
1472 * @param parentspecifier either a CSID or one of the urn forms
1473 * @param itemspecifier either a CSID or one of the urn forms
1475 * @return the multipart output
1478 @Path("{csid}/items/{itemcsid}")
1479 public byte[] updateAuthorityItem(
1480 @Context ResourceMap resourceMap,
1481 @Context UriInfo uriInfo,
1482 @PathParam("csid") String parentSpecifier,
1483 @PathParam("itemcsid") String itemSpecifier,
1484 String xmlPayload) {
1485 uriInfo = new UriInfoWrapper(uriInfo);
1486 PoxPayloadOut result = null;
1489 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1490 result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1491 AuthorityServiceUtils.UPDATE_REV, // passing TRUE so rev num increases, passing
1492 AuthorityServiceUtils.NO_CHANGE, // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1493 AuthorityServiceUtils.NO_CHANGE); // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1494 } catch (Exception e) {
1495 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1498 return result.getBytes();
1504 * Delete authorityItem.
1506 * @param parentIdentifier the parentcsid
1507 * @param itemIdentifier the itemcsid
1509 * @return the response
1512 @Path("{csid}/items/{itemcsid}")
1513 public Response deleteAuthorityItem(
1514 @Context UriInfo uriInfo,
1515 @PathParam("csid") String parentIdentifier,
1516 @PathParam("itemcsid") String itemIdentifier) {
1517 uriInfo = new UriInfoWrapper(uriInfo);
1518 Response result = null;
1520 ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1521 ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1522 if (logger.isDebugEnabled()) {
1523 logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1527 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1528 deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1529 result = Response.status(HttpResponseCodes.SC_OK).build();
1530 } catch (Exception e) {
1531 throw bigReThrow(e, ServiceMessages.DELETE_FAILED + " itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1539 * @param existingCtx
1540 * @param parentIdentifier
1541 * @param itemIdentifier
1544 public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1545 String parentIdentifier,
1546 String itemIdentifier,
1547 boolean shouldUpdateRevNumber
1548 ) throws Exception {
1549 boolean result = true;
1551 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1552 if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1553 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1554 ctx.setProperties(existingCtx.getProperties());
1557 String parentcsid = null;
1559 parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1560 } catch (DocumentNotFoundException de) {
1561 String msg = String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1562 parentIdentifier, itemIdentifier);
1566 String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1568 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1569 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1570 result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1576 @Path("{csid}/items/{itemcsid}/" + hierarchy)
1577 @Produces("application/xml")
1578 public String getHierarchy(
1579 @PathParam("csid") String parentIdentifier,
1580 @PathParam("itemcsid") String itemIdentifier,
1581 @Context UriInfo uriInfo) throws Exception {
1582 uriInfo = new UriInfoWrapper(uriInfo);
1583 String result = null;
1587 // 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...?
1589 String calledUri = uriInfo.getPath();
1590 String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1591 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1593 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1594 String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1596 String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1597 if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1598 result = Hierarchy.surface(ctx, itemcsid, uri);
1600 result = Hierarchy.dive(ctx, itemcsid, uri);
1602 } catch (Exception e) {
1603 throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1614 public String getItemDocType(String tenantId) {
1615 return getDocType(tenantId, getItemServiceName());
1619 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1620 * for the current resource, for all tenants
1622 * @return a map of URI templates for the current resource, for all tenants
1625 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1626 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1627 super.getUriRegistryEntries();
1628 List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1629 for (String tenantId : tenantIds) {
1630 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1632 return uriRegistryEntriesMap;
1639 public ServiceDescription getDescription(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1640 ServiceDescription result = super.getDescription(ctx);
1641 result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));
1645 public Response createAuthority(String xmlPayload) {
1646 return this.createAuthority(null, null, xmlPayload);
1649 protected String getCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, Specifier specifier) throws Exception {
1653 ctx = createServiceContext(getServiceName());
1656 if (specifier.form == SpecifierForm.CSID) {
1657 csid = specifier.value;
1659 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, specifier.value);
1660 csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);