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.UriTemplateRegistry;
62 import org.collectionspace.services.common.UriTemplateRegistryKey;
63 import org.collectionspace.services.common.api.RefName;
64 import org.collectionspace.services.common.api.Tools;
65 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
66 import org.collectionspace.services.common.authorityref.AuthorityRefList;
67 import org.collectionspace.services.common.context.JaxRsContext;
68 import org.collectionspace.services.common.context.MultipartServiceContext;
69 import org.collectionspace.services.common.context.RemoteServiceContext;
70 import org.collectionspace.services.common.context.ServiceBindingUtils;
71 import org.collectionspace.services.common.context.ServiceContext;
72 import org.collectionspace.services.common.document.DocumentException;
73 import org.collectionspace.services.common.document.DocumentFilter;
74 import org.collectionspace.services.common.document.DocumentHandler;
75 import org.collectionspace.services.common.document.DocumentNotFoundException;
76 import org.collectionspace.services.common.document.DocumentReferenceException;
77 import org.collectionspace.services.common.document.DocumentWrapper;
78 import org.collectionspace.services.common.document.Hierarchy;
79 import org.collectionspace.services.common.query.QueryManager;
80 import org.collectionspace.services.common.repository.RepositoryClient;
81 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
82 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
83 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
84 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
85 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
87 import org.collectionspace.services.config.ClientType;
88 import org.collectionspace.services.config.service.ServiceBindingType;
89 import org.collectionspace.services.jaxb.AbstractCommonList;
90 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
91 import org.collectionspace.services.lifecycle.TransitionDef;
92 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
93 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
94 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentFilter;
95 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
96 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
97 import org.collectionspace.services.workflow.WorkflowCommon;
98 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
99 import org.collectionspace.services.description.ServiceDescription;
101 import org.jboss.resteasy.util.HttpResponseCodes;
102 import org.nuxeo.ecm.core.api.DocumentModel;
103 import org.nuxeo.ecm.core.api.DocumentModelList;
104 import org.slf4j.Logger;
105 import org.slf4j.LoggerFactory;
106 import org.w3c.dom.Element;
109 * The Class AuthorityResource.
112 @SuppressWarnings({"rawtypes", "unchecked"})
113 @Consumes("application/xml")
114 @Produces("application/xml")
115 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
116 extends NuxeoBasedResource {
118 final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
120 final static String SEARCH_TYPE_TERMSTATUS = "ts";
121 public final static String hierarchy = "hierarchy";
123 protected Class<AuthCommon> authCommonClass;
124 protected Class<?> resourceClass;
125 protected String authorityCommonSchemaName;
126 protected String authorityItemCommonSchemaName;
127 final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed? I see no references to it.
129 final static String FETCH_SHORT_ID = "_fetch_";
130 public final static String PARENT_WILDCARD = "_ALL_";
131 protected static final boolean DONT_INCLUDE_ITEMS = false;
132 protected static final boolean INCLUDE_ITEMS = true;
135 * Instantiates a new Authority resource.
137 public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
138 String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
139 this.authCommonClass = authCommonClass;
140 this.resourceClass = resourceClass;
141 this.authorityCommonSchemaName = authorityCommonSchemaName;
142 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
145 public abstract String getItemServiceName();
147 public abstract String getItemTermInfoGroupXPathBase();
150 protected String getVersionString() {
151 return "$LastChangedRevision: 2617 $";
155 public Class<AuthCommon> getCommonPartClass() {
156 return authCommonClass;
160 * Creates the item document handler.
163 * @param inAuthority the in vocabulary
165 * @return the document handler
167 * @throws Exception the exception
169 protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
170 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
171 String inAuthority, String containerShortIdentifier)
173 String authorityRefNameBase;
174 AuthorityItemDocumentModelHandler<?> docHandler;
176 if (containerShortIdentifier == null) {
177 authorityRefNameBase = null;
179 ServiceContext<PoxPayloadIn, PoxPayloadOut> containerCtx = createServiceContext(getServiceName());
180 if (containerShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
181 if (ctx.getCurrentRepositorySession() != null) {
182 containerCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
184 // Get from parent document
185 containerShortIdentifier = getAuthShortIdentifier(containerCtx, inAuthority);
187 authorityRefNameBase = buildAuthorityRefNameBase(containerCtx, containerShortIdentifier);
190 docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
191 ctx.getCommonPartLabel(getItemServiceName()),
193 // FIXME - Richard and Aron think the following three lines should
194 // be in the constructor for the AuthorityItemDocumentModelHandler
195 // because all three are required fields.
196 docHandler.setInAuthority(inAuthority);
197 docHandler.setAuthorityRefNameBase(authorityRefNameBase);
198 docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
202 public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
203 throws DocumentNotFoundException, DocumentException {
204 String shortIdentifier = null;
207 AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
208 shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
209 } catch (Exception e) {
210 if (logger.isDebugEnabled()) {
211 logger.debug("Caught exception ", e);
213 throw new DocumentException(e);
216 return shortIdentifier;
219 protected String buildAuthorityRefNameBase(
220 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
221 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
222 ctx.getServiceName(),
223 null, // Only use shortId form!!!
224 shortIdentifier, null);
225 return authority.toString();
228 public static class CsidAndShortIdentifier {
230 String shortIdentifier;
233 protected String lookupParentCSID(String parentspecifier, String method,
234 String op, UriInfo uriInfo) throws Exception {
235 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(NULL_CONTEXT,
236 parentspecifier, method, op, uriInfo);
237 return tempResult.CSID;
240 protected String lookupParentCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentspecifier, String method,
241 String op, UriInfo uriInfo) throws Exception {
242 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
243 parentspecifier, method, op, uriInfo);
244 return tempResult.CSID;
248 private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
249 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx, // Ok to be null
250 String parentIdentifier,
255 CsidAndShortIdentifier result = new CsidAndShortIdentifier();
256 Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
259 String parentShortIdentifier;
260 if (parentSpec.form == SpecifierForm.CSID) {
261 parentShortIdentifier = null;
262 parentcsid = parentSpec.value;
263 // Uncomment when app layer is ready to integrate
264 // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
265 parentShortIdentifier = FETCH_SHORT_ID;
267 parentShortIdentifier = parentSpec.value;
268 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
269 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
270 CoreSessionInterface repoSession = null;
271 if (existingCtx != null) {
272 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession(); // We want to use the thread's current repo session
274 parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
277 result.CSID = parentcsid;
278 result.shortIdentifier = parentShortIdentifier;
283 public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemspecifier, String parentcsid, String method, String op)
287 Specifier itemSpec = Specifier.getSpecifier(itemspecifier, method, op);
288 if (itemSpec.form == SpecifierForm.CSID) {
289 itemcsid = itemSpec.value;
291 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
292 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
293 CoreSessionInterface repoSession = null;
294 if (existingContext != null) {
295 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession(); // We want to use the thread's current repo session
297 itemcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
304 * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then
305 * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
306 * Resource. They then call this method on that resource.
309 public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item)
310 throws Exception, DocumentNotFoundException {
314 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
315 // Ensure we have the right context.
316 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
318 // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
319 NuxeoRepositoryClientImpl client = (NuxeoRepositoryClientImpl)getRepositoryClient(ctx);
320 String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
322 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
323 ctx = createServiceContext(getItemServiceName());
324 DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
325 DocumentModel docModel = docWrapper.getWrappedObject();
331 public Response createAuthority(
332 @Context ResourceMap resourceMap,
333 @Context UriInfo uriInfo,
336 // 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
337 // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
338 // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
339 // the code that creates new authorities. The authority document model handler will first check for authorities with the same short id before
340 // trying to create a new authority.
342 synchronized(AuthorityResource.class) {
344 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
345 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
346 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
348 String csid = getRepositoryClient(ctx).create(ctx, handler);
349 UriBuilder path = UriBuilder.fromResource(resourceClass);
350 path.path("" + csid);
351 Response response = Response.created(path.build()).build();
353 } catch (Exception e) {
354 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
359 protected boolean supportsReplicating(String tenantId, String serviceName) {
360 boolean result = false;
362 ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
363 result = sb.isSupportsReplicating();
369 * Synchronizes the authority and its items/terms with a Shared Authority Server.
371 * @param specifier either a CSID or one of the urn forms
373 * @return the authority
377 public byte[] synchronize(
378 @Context Request request,
379 @Context UriInfo uriInfo,
380 @PathParam("csid") String identifier) {
381 uriInfo = new UriInfoWrapper(uriInfo);
383 boolean neededSync = false;
384 PoxPayloadOut payloadOut = null;
388 // Prevent multiple SAS synchronizations from occurring simultaneously by synchronizing this method.
390 synchronized(AuthorityResource.class) {
392 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
394 * Make sure this authority service supports synchronization
396 if (supportsReplicating(ctx.getTenantId(), ctx.getServiceName()) == false) {
397 throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
399 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
400 specifier = Specifier.getSpecifier(identifier, "getAuthority", "GET");
401 handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
402 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
403 payloadOut = ctx.getOutput();
404 } catch (Exception e) {
405 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, identifier);
409 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
411 if (neededSync == true) {
412 result = payloadOut.getBytes();
414 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
415 specifier.value).getBytes();
416 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
417 throw new CSWebApplicationException(response);
425 * Builds a cached JAX-RS response.
427 protected Response buildResponse(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, PoxPayloadOut payloadOut) {
428 Response result = null;
430 ResponseBuilder responseBuilder = Response.ok(payloadOut.getBytes());
431 this.setCacheControl(ctx, responseBuilder);
432 result = responseBuilder.build();
438 * Gets the authority.
440 * @param specifier either a CSID or one of the urn forms
442 * @return the authority
448 @Context Request request,
449 @Context UriInfo uriInfo,
450 @PathParam("csid") String specifier) {
451 Response result = null;
452 uriInfo = new UriInfoWrapper(uriInfo);
455 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(request, uriInfo);
456 PoxPayloadOut payloadout = getAuthority(ctx, request, uriInfo, specifier, DONT_INCLUDE_ITEMS);
457 result = buildResponse(ctx, payloadout);
458 } catch (Exception e) {
459 throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
462 if (result == null) {
463 Response response = Response.status(Response.Status.NOT_FOUND).entity(
464 "GET request failed. The requested Authority specifier:" + specifier + ": was not found.").type(
465 "text/plain").build();
466 throw new CSWebApplicationException(response);
472 protected PoxPayloadOut getAuthority(
473 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
477 boolean includeItems) throws Exception {
478 uriInfo = new UriInfoWrapper(uriInfo);
479 PoxPayloadOut payloadout = null;
481 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> docHandler = createDocumentHandler(ctx);
482 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
483 if (spec.form == SpecifierForm.CSID) {
484 if (logger.isDebugEnabled()) {
485 logger.debug("getAuthority with csid=" + spec.value);
487 getRepositoryClient(ctx).get(ctx, spec.value, docHandler);
489 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
490 DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
491 docHandler.setDocumentFilter(myFilter);
492 getRepositoryClient(ctx).get(ctx, docHandler);
495 payloadout = ctx.getOutput();
496 if (includeItems == true) {
497 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
498 payloadout.addPart(PoxPayload.ABSTRACT_COMMON_LIST_ROOT_ELEMENT_LABEL, itemsList);
505 * Finds and populates the authority list.
509 * @return the authority list
512 @Produces("application/xml")
513 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.
514 uriInfo = new UriInfoWrapper(uriInfo);
515 AbstractCommonList result = null;
518 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
519 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
521 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
522 DocumentFilter myFilter = handler.getDocumentFilter();
523 // Need to make the default sort order for authority items
524 // be on the displayName field
525 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
526 if (orderBy == null || orderBy.isEmpty()) {
527 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
528 + AuthorityItemJAXBSchema.DISPLAY_NAME;
529 myFilter.setOrderByClause(qualifiedDisplayNameField);
531 String nameQ = queryParams.getFirst("refName");
533 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
535 //getRepositoryClient(ctx).getFiltered(ctx, handler); # Something here?
536 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
537 result = search(ctx, handler, uriInfo, orderBy, null, advancedSearch, null);
538 result = handler.getCommonPartList();
539 } catch (Exception e) {
540 throw bigReThrow(e, ServiceMessages.GET_FAILED);
547 * Overriding this methods to see if we should update the revision number during the update. We don't
548 * want to update the rev number of synchronization operations.
551 protected PoxPayloadOut update(String csid,
552 PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
553 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
555 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
556 Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
557 if (shouldUpdateRev != null) {
558 handler.setShouldUpdateRevNumber(shouldUpdateRev);
560 getRepositoryClient(ctx).update(ctx, csid, handler);
561 return ctx.getOutput();
567 * @param specifier the csid or id
569 * @return the multipart output
573 public byte[] updateAuthority(
574 @Context Request request,
575 @Context ResourceMap resourceMap,
576 @Context UriInfo uriInfo,
577 @PathParam("csid") String specifier,
579 PoxPayloadOut result = null;
581 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
582 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
583 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
584 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
586 if (spec.form == SpecifierForm.CSID) {
589 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
590 csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
592 getRepositoryClient(ctx).update(ctx, csid, handler);
593 result = ctx.getOutput();
594 } catch (Exception e) {
595 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
597 return result.getBytes();
601 * Delete all the items in an authority list.
608 @Path("{csid}/items")
609 public Response deleteAuthorityItemList(@PathParam("csid") String specifier,
610 @Context UriInfo uriInfo) {
611 uriInfo = new UriInfoWrapper(uriInfo);
614 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
615 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
617 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
619 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
621 // Delete all the items one by one
623 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
624 for (ListItem item : itemsList.getListItem()) {
625 deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
627 } catch (Throwable t) {
628 repoSession.setTransactionRollbackOnly();
631 repoClient.releaseRepositorySession(ctx, repoSession);
634 return Response.status(HttpResponseCodes.SC_OK).build();
635 } catch (Exception e) {
636 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
643 * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
645 * @return the response
649 public Response deleteAuthority( // # Delete this authority and all of it's items.
650 @Context Request request,
651 @Context UriInfo uriInfo,
652 @PathParam("csid") String specifier) {
653 uriInfo = new UriInfoWrapper(uriInfo);
655 if (logger.isDebugEnabled()) {
656 logger.debug("deleteAuthority with specifier=" + specifier);
660 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
661 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
662 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
664 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
666 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
668 // First try to delete all the items
670 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
671 for (ListItem item : itemsList.getListItem()) {
672 deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
676 // Lastly, delete the parent/container
678 if (spec.form == SpecifierForm.CSID) {
679 if (logger.isDebugEnabled()) {
680 logger.debug("deleteAuthority with csid=" + spec.value);
682 ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
683 getRepositoryClient(ctx).delete(ctx, spec.value, handler);
685 if (logger.isDebugEnabled()) {
686 logger.debug("deleteAuthority with specifier=" + spec.value);
688 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
689 getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
691 } catch (Throwable t) {
692 repoSession.setTransactionRollbackOnly();
695 repoClient.releaseRepositorySession(ctx, repoSession);
698 return Response.status(HttpResponseCodes.SC_OK).build();
699 } catch (Exception e) {
700 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
704 private String getCsid(ListItem item) {
705 String result = null;
707 for (Element ele : item.getAny()) {
708 String elementName = ele.getTagName().toLowerCase();
709 if (elementName.equals("csid")) {
710 result = ele.getTextContent();
721 * @param parentspecifier - ID of the container. Can be URN or CSID form
722 * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
723 * @param isProposed - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
727 protected Response createAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentIdentifier,
728 boolean shouldUpdateRevNumber,
730 boolean isSasItem) throws Exception {
731 Response result = null;
733 // Note: must have the parentShortId, to do the create.
734 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
735 AuthorityItemDocumentModelHandler handler =
736 (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
737 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
738 handler.setIsProposed(isProposed);
739 handler.setIsSASItem(isSasItem);
740 // Make the client call
741 String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
743 // Build the JAX-RS response
744 UriBuilder path = UriBuilder.fromResource(resourceClass);
745 path.path(parent.CSID + "/items/" + itemcsid);
746 result = Response.created(path.build()).build();
751 public PoxPayloadOut updateAuthorityItem(
752 ServiceContext<PoxPayloadIn, PoxPayloadOut> itemServiceCtx, // Ok to be null. Will be null on PUT calls, but not on sync calls
753 ResourceMap resourceMap,
755 String parentspecifier,
756 String itemspecifier,
757 PoxPayloadIn theUpdate,
758 boolean shouldUpdateRevNumber,
762 PoxPayloadOut result = null;
764 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
765 String parentcsid = csidAndShortId.CSID;
766 String parentShortId = csidAndShortId.shortIdentifier;
768 // If the itemServiceCtx context is not null, use it. Otherwise, create a new context
770 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
772 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
774 ctx.setInput(theUpdate); // the update payload
777 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
779 // We omit the parentShortId, only needed when doing a create...
780 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
781 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
783 // Update the SAS fields if either value is non-null
785 boolean updateSASFields = isProposed != null || isSASItem != null;
786 handler.setshouldUpdateSASFields(updateSASFields);
787 if (updateSASFields == true) {
788 handler.setshouldUpdateSASFields(true);
789 if (isProposed != null) {
790 handler.setIsProposed(isProposed);
792 if (isSASItem != null) {
793 handler.setIsSASItem(isSASItem);
797 getRepositoryClient(ctx).update(ctx, itemcsid, handler);
798 result = ctx.getOutput();
804 * Called with an existing context.
806 * @param parentIdentifier
811 public Response createAuthorityItemWithParentContext(ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx,
812 String parentIdentifier,
814 boolean shouldUpdateRevNumber,
816 boolean isSASItem) throws Exception {
817 Response result = null;
819 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
820 parentCtx.getResourceMap(), parentCtx.getUriInfo());
821 if (parentCtx.getCurrentRepositorySession() != null) {
822 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
824 result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
829 /*************************************************************************
830 * Create an AuthorityItem - this is a sub-resource of Authority
831 * @param specifier either a CSID or one of the urn forms
832 * @return Authority item response
833 *************************************************************************/
835 @Path("{csid}/items")
836 public Response createAuthorityItem(
837 @Context ResourceMap resourceMap,
838 @Context UriInfo uriInfo,
839 @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
841 uriInfo = new UriInfoWrapper(uriInfo);
842 Response result = null;
845 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
846 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
847 result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
848 AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
849 } catch (Exception e) {
850 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
857 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
858 public byte[] getItemWorkflow(
859 @PathParam("csid") String csid,
860 @PathParam("itemcsid") String itemcsid) {
861 PoxPayloadOut result = null;
864 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
865 String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
867 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
868 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
869 ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
870 getRepositoryClient(ctx).get(ctx, itemcsid, handler);
871 result = ctx.getOutput();
872 } catch (Exception e) {
873 throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
875 return result.getBytes();
878 //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
879 // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
881 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
882 public byte[] updateItemWorkflowWithTransition(
883 @Context UriInfo uriInfo,
884 @PathParam("csid") String parentIdentifier,
885 @PathParam("itemcsid") String itemIdentifier,
886 @PathParam("transition") String transition) {
887 uriInfo = new UriInfoWrapper(uriInfo);
888 PoxPayloadOut result = null;
891 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
892 result = updateItemWorkflowWithTransition(ctx,
893 parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
894 } catch (Exception e) {
895 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
898 return result.getBytes();
902 * Update an authority item's workflow state.
903 * @param existingContext
908 * @throws DocumentReferenceException
910 public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
911 String parentIdentifier,
912 String itemIdentifier,
914 boolean updateRevNumber) throws DocumentReferenceException {
915 PoxPayloadOut result = null;
919 // We need CSIDs for both the parent authority and the authority item
921 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
922 String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
925 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
927 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(),
928 WorkflowClient.SERVICE_COMMONPART_NAME);
929 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
930 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
931 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
934 // Create a service context and document handler for the target resource -not the workflow resource itself.
936 ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
937 AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
938 targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
939 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
941 // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
943 String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
944 ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
946 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
947 TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
948 if (transitionDef == null) {
949 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
950 itemIdentifier, transition));
952 ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
954 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
955 getRepositoryClient(ctx).update(ctx, itemCsid, handler);
956 result = ctx.getOutput();
957 } catch (DocumentReferenceException de) {
959 } catch (Exception e) {
960 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
966 private PoxPayloadOut getAuthorityItem(
967 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
968 String parentIdentifier,
969 String itemIdentifier) throws Exception {
970 PoxPayloadOut result = null;
972 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
973 // We omit the parentShortId, only needed when doing a create...
974 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
976 Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
977 if (itemSpec.form == SpecifierForm.CSID) {
978 // TODO should we assert that the item is in the passed vocab?
979 getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
981 String itemWhereClause =
982 RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
983 DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
984 handler.setDocumentFilter(myFilter);
985 getRepositoryClient(ctx).get(ctx, handler);
988 result = (PoxPayloadOut) ctx.getOutput();
989 if (result != null) {
990 String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
991 if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
992 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
993 itemSpec.value, inAuthority, parentcsid));
1000 public PoxPayloadOut getAuthorityItemWithExistingContext(
1001 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1002 String parentIdentifier,
1003 String itemIdentifier) throws Exception {
1004 PoxPayloadOut result = null;
1006 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
1007 if (existingCtx.getCurrentRepositorySession() != null) {
1008 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
1009 ctx.setProperties(existingCtx.getProperties());
1011 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1017 * Gets the authority item.
1019 * @param parentspecifier either a CSID or one of the urn forms
1020 * @param itemspecifier either a CSID or one of the urn forms
1022 * @return the authority item
1025 @Path("{csid}/items/{itemcsid}")
1026 public byte[] getAuthorityItem(
1027 @Context Request request,
1028 @Context UriInfo uriInfo,
1029 @Context ResourceMap resourceMap,
1030 @PathParam("csid") String parentIdentifier,
1031 @PathParam("itemcsid") String itemIdentifier) {
1032 uriInfo = new UriInfoWrapper(uriInfo);
1033 PoxPayloadOut result = null;
1035 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx =
1036 (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1038 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1039 ctx.setJaxRsContext(jaxRsContext);
1041 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1042 } catch (DocumentNotFoundException dnf) {
1043 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1044 } catch (Exception e) {
1045 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1048 return result.getBytes();
1052 * Most of the authority child classes will/should use this implementation. However, the Vocabulary service's item schema is
1053 * different enough that it will have to override this method in it's resource class.
1056 protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1057 String result = null;
1059 result = NuxeoUtils.getPrimaryElPathPropertyName(
1060 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1061 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1067 protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1068 String result = null;
1070 result = NuxeoUtils.getMultiElPathPropertyName(
1071 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1072 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1078 * Gets the authorityItem list for the specified authority
1079 * If partialPerm is specified, keywords will be ignored.
1081 * @param authorityIdentifier either a CSID or one of the urn forms
1082 * @param partialTerm if non-null, matches partial terms
1083 * @param keywords if non-null, matches terms in the keyword index for items
1084 * @param ui passed to include additional parameters, like pagination controls
1087 public AbstractCommonList getAuthorityItemList(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1088 String authorityIdentifier,
1089 UriInfo uriInfo) throws Exception {
1090 AbstractCommonList result = null;
1092 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1093 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1094 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
1095 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
1096 ctx.setProperties(existingContext.getProperties());
1099 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
1100 String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
1101 String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
1102 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
1103 String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
1105 // For the wildcard case, parentcsid is null, but docHandler will deal with this.
1106 // We omit the parentShortId, only needed when doing a create...
1107 String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
1108 lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
1109 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
1110 createItemDocumentHandler(ctx, parentcsid, null);
1112 DocumentFilter myFilter = handler.getDocumentFilter();
1113 // If we are not wildcarding the parent, add a restriction
1114 if (parentcsid != null) {
1115 myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
1116 + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
1117 + "'" + parentcsid + "'",
1118 IQueryManager.SEARCH_QUALIFIER_AND);
1121 if (Tools.notBlank(termStatus)) {
1122 // Start with the qualified termStatus field
1123 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
1124 + AuthorityItemJAXBSchema.TERM_STATUS;
1125 String[] filterTerms = termStatus.trim().split("\\|");
1126 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
1127 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
1130 result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
1136 * Gets the authorityItem list for the specified authority
1137 * If partialPerm is specified, keywords will be ignored.
1139 * @param authorityIdentifier either a CSID or one of the urn forms
1140 * @param partialTerm if non-null, matches partial terms
1141 * @param keywords if non-null, matches terms in the keyword index for items
1142 * @param ui passed to include additional parameters, like pagination controls
1144 * @return the authorityItem list
1147 @Path("{csid}/items")
1148 @Produces("application/xml")
1149 public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
1150 @Context UriInfo uriInfo) {
1151 uriInfo = new UriInfoWrapper(uriInfo);
1152 AbstractCommonList result = null;
1155 result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);
1156 } catch (Exception e) {
1157 throw bigReThrow(e, ServiceMessages.LIST_FAILED);
1164 * @return the name of the property used to specify references for items in this type of
1165 * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
1166 * Some types (like Vocabulary) use a separate property.
1168 protected String getRefPropName() {
1169 return ServiceBindingUtils.AUTH_REF_PROP;
1173 * Gets the entities referencing this Authority item instance. The service type
1174 * can be passed as a query param "type", and must match a configured type
1175 * for the service bindings. If not set, the type defaults to
1176 * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1178 * @param parentspecifier either a CSID or one of the urn forms
1179 * @param itemspecifier either a CSID or one of the urn forms
1182 * @return the info for the referencing objects
1185 @Path("{csid}/items/{itemcsid}/refObjs")
1186 @Produces("application/xml")
1187 public AuthorityRefDocList getReferencingObjects(
1188 @PathParam("csid") String parentSpecifier,
1189 @PathParam("itemcsid") String itemSpecifier,
1190 @Context UriTemplateRegistry uriTemplateRegistry,
1191 @Context UriInfo uriInfo) {
1192 uriInfo = new UriInfoWrapper(uriInfo);
1193 AuthorityRefDocList authRefDocList = null;
1195 authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriTemplateRegistry, uriInfo);
1196 } catch (Exception e) {
1197 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1200 if (authRefDocList == null) {
1201 Response response = Response.status(Response.Status.NOT_FOUND).entity(
1202 "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1203 "text/plain").build();
1204 throw new CSWebApplicationException(response);
1206 return authRefDocList;
1209 public AuthorityRefDocList getReferencingObjects(
1210 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1211 String parentspecifier,
1212 String itemspecifier,
1213 UriTemplateRegistry uriTemplateRegistry,
1214 UriInfo uriInfo) throws Exception {
1215 //uriInfo = new UriInfoWrapper(uriInfo);
1216 AuthorityRefDocList authRefDocList = null;
1218 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1219 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1221 // Merge parts of existing context with our new context
1223 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1224 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If one exists, use the existing repo session
1225 ctx.setProperties(existingContext.getProperties());
1228 String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1229 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1231 // Remove the "type" property from the query params
1232 List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1233 if (serviceTypes == null || serviceTypes.isEmpty()) {
1234 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1237 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1238 authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
1240 return authRefDocList;
1244 * Gets the authority terms used in the indicated Authority item.
1246 * @param parentspecifier either a CSID or one of the urn forms
1247 * @param itemspecifier either a CSID or one of the urn forms
1248 * @param ui passed to include additional parameters, like pagination controls
1250 * @return the authority refs for the Authority item.
1253 @Path("{csid}/items/{itemcsid}/authorityrefs")
1254 @Produces("application/xml")
1255 public AuthorityRefList getAuthorityItemAuthorityRefs(
1256 @PathParam("csid") String parentspecifier,
1257 @PathParam("itemcsid") String itemspecifier,
1258 @Context UriInfo uriInfo) {
1259 uriInfo = new UriInfoWrapper(uriInfo);
1260 AuthorityRefList authRefList = null;
1263 // Note that we have to create the service context for the Items, not the main service
1264 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1265 String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1266 // We omit the parentShortId, only needed when doing a create...
1267 DocumentModelHandler<?, AbstractCommonList> handler =
1268 (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1270 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1272 List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1273 authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1274 } catch (Exception e) {
1275 throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1282 * Synchronizes a local authority item with a share authority server (SAS) item.
1284 * @param parentIdentifier
1285 * @param itemIdentifier
1289 private PoxPayloadOut synchronizeItem(
1290 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1291 String parentIdentifier,
1292 String itemIdentifier,
1293 boolean syncHierarchicalRelationships) throws Exception {
1294 PoxPayloadOut result = null;
1295 AuthorityItemSpecifier specifier;
1296 boolean neededSync = false;
1298 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1299 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1300 handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1301 handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1302 handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1303 // Create an authority item specifier
1304 Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1305 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1306 specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1308 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1309 if (neededSync == true) {
1310 result = (PoxPayloadOut) ctx.getOutput();
1317 * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1318 * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1319 * local authority. The parent context was created for the authority (parent) because the sync started there.
1320 * @param existingCtx
1321 * @param parentIdentifier
1322 * @param itemIdentifier
1326 public PoxPayloadOut synchronizeItemWithExistingContext(
1327 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1328 String parentIdentifier,
1329 String itemIdentifier,
1330 boolean syncHierarchicalRelationships
1331 ) throws Exception {
1332 PoxPayloadOut result = null;
1334 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1335 existingCtx.getResourceMap(),
1336 existingCtx.getUriInfo());
1337 if (existingCtx.getCurrentRepositorySession() != null) {
1338 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1341 result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1347 * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1349 * @param specifier either CSIDs and/or one of the urn forms
1351 * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1354 @Path("{csid}/items/{itemcsid}/sync")
1355 public byte[] synchronizeItem(
1356 @Context ResourceMap resourceMap,
1357 @Context UriInfo uriInfo,
1358 @PathParam("csid") String parentIdentifier,
1359 @PathParam("itemcsid") String itemIdentifier) {
1360 uriInfo = new UriInfoWrapper(uriInfo);
1362 boolean neededSync = false;
1363 PoxPayloadOut payloadOut = null;
1366 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1367 payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1368 if (payloadOut != null) {
1371 } catch (Exception e) {
1372 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1376 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
1378 if (neededSync == true) {
1379 result = payloadOut.getBytes();
1381 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1382 itemIdentifier).getBytes();
1383 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1384 throw new CSWebApplicationException(response);
1391 * Update authorityItem.
1393 * @param parentspecifier either a CSID or one of the urn forms
1394 * @param itemspecifier either a CSID or one of the urn forms
1396 * @return the multipart output
1399 @Path("{csid}/items/{itemcsid}")
1400 public byte[] updateAuthorityItem(
1401 @Context ResourceMap resourceMap,
1402 @Context UriInfo uriInfo,
1403 @PathParam("csid") String parentSpecifier,
1404 @PathParam("itemcsid") String itemSpecifier,
1405 String xmlPayload) {
1406 uriInfo = new UriInfoWrapper(uriInfo);
1407 PoxPayloadOut result = null;
1410 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1411 result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1412 AuthorityServiceUtils.UPDATE_REV, // passing TRUE so rev num increases, passing
1413 AuthorityServiceUtils.NO_CHANGE, // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1414 AuthorityServiceUtils.NO_CHANGE); // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1415 } catch (Exception e) {
1416 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1419 return result.getBytes();
1425 * Delete authorityItem.
1427 * @param parentIdentifier the parentcsid
1428 * @param itemIdentifier the itemcsid
1430 * @return the response
1433 @Path("{csid}/items/{itemcsid}")
1434 public Response deleteAuthorityItem(
1435 @Context UriInfo uriInfo,
1436 @PathParam("csid") String parentIdentifier,
1437 @PathParam("itemcsid") String itemIdentifier) {
1438 uriInfo = new UriInfoWrapper(uriInfo);
1439 Response result = null;
1441 ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1442 ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1443 if (logger.isDebugEnabled()) {
1444 logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1448 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1449 deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1450 result = Response.status(HttpResponseCodes.SC_OK).build();
1451 } catch (Exception e) {
1452 throw bigReThrow(e, ServiceMessages.DELETE_FAILED + " itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1460 * @param existingCtx
1461 * @param parentIdentifier
1462 * @param itemIdentifier
1465 public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1466 String parentIdentifier,
1467 String itemIdentifier,
1468 boolean shouldUpdateRevNumber
1469 ) throws Exception {
1470 boolean result = true;
1472 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1473 if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1474 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1475 ctx.setProperties(existingCtx.getProperties());
1478 String parentcsid = null;
1480 parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1481 } catch (DocumentNotFoundException de) {
1482 String msg = String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1483 parentIdentifier, itemIdentifier);
1487 String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1489 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1490 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1491 result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1497 @Path("{csid}/items/{itemcsid}/" + hierarchy)
1498 @Produces("application/xml")
1499 public String getHierarchy(
1500 @PathParam("csid") String parentIdentifier,
1501 @PathParam("itemcsid") String itemIdentifier,
1502 @Context UriInfo uriInfo) throws Exception {
1503 uriInfo = new UriInfoWrapper(uriInfo);
1504 String result = null;
1508 // 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...?
1510 String calledUri = uriInfo.getPath();
1511 String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1512 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1514 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1515 String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1517 String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1518 if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1519 result = Hierarchy.surface(ctx, itemcsid, uri);
1521 result = Hierarchy.dive(ctx, itemcsid, uri);
1523 } catch (Exception e) {
1524 throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1535 public String getItemDocType(String tenantId) {
1536 return getDocType(tenantId, getItemServiceName());
1540 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1541 * for the current resource, for all tenants
1543 * @return a map of URI templates for the current resource, for all tenants
1546 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1547 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1548 super.getUriRegistryEntries();
1549 List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1550 for (String tenantId : tenantIds) {
1551 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1553 return uriRegistryEntriesMap;
1560 public ServiceDescription getDescription(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1561 ServiceDescription result = super.getDescription(ctx);
1562 result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));
1566 public Response createAuthority(String xmlPayload) {
1567 return this.createAuthority(null, null, xmlPayload);