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;
52 import org.collectionspace.services.common.CSWebApplicationException;
53 import org.collectionspace.services.common.NuxeoBasedResource;
54 import org.collectionspace.services.common.ResourceMap;
55 import org.collectionspace.services.common.ServiceMain;
56 import org.collectionspace.services.common.ServiceMessages;
57 import org.collectionspace.services.common.StoredValuesUriTemplate;
58 import org.collectionspace.services.common.UriInfoWrapper;
59 import org.collectionspace.services.common.UriTemplateFactory;
60 import org.collectionspace.services.common.UriTemplateRegistry;
61 import org.collectionspace.services.common.UriTemplateRegistryKey;
62 import org.collectionspace.services.common.api.RefName;
63 import org.collectionspace.services.common.api.Tools;
64 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
65 import org.collectionspace.services.common.authorityref.AuthorityRefList;
66 import org.collectionspace.services.common.context.JaxRsContext;
67 import org.collectionspace.services.common.context.MultipartServiceContext;
68 import org.collectionspace.services.common.context.RemoteServiceContext;
69 import org.collectionspace.services.common.context.ServiceBindingUtils;
70 import org.collectionspace.services.common.context.ServiceContext;
71 import org.collectionspace.services.common.document.DocumentException;
72 import org.collectionspace.services.common.document.DocumentFilter;
73 import org.collectionspace.services.common.document.DocumentHandler;
74 import org.collectionspace.services.common.document.DocumentNotFoundException;
75 import org.collectionspace.services.common.document.DocumentReferenceException;
76 import org.collectionspace.services.common.document.DocumentWrapper;
77 import org.collectionspace.services.common.document.Hierarchy;
78 import org.collectionspace.services.common.query.QueryManager;
79 import org.collectionspace.services.common.repository.RepositoryClient;
80 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
81 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
82 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
83 import org.collectionspace.services.config.ClientType;
84 import org.collectionspace.services.config.service.ServiceBindingType;
85 import org.collectionspace.services.jaxb.AbstractCommonList;
86 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
87 import org.collectionspace.services.lifecycle.TransitionDef;
88 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
89 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
90 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentFilter;
91 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
92 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
93 import org.collectionspace.services.workflow.WorkflowCommon;
94 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
95 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
96 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
97 import org.collectionspace.services.description.ServiceDescription;
98 import org.jboss.resteasy.util.HttpResponseCodes;
99 import org.nuxeo.ecm.core.api.DocumentModel;
100 import org.nuxeo.ecm.core.api.DocumentModelList;
101 import org.slf4j.Logger;
102 import org.slf4j.LoggerFactory;
103 import org.w3c.dom.Element;
106 * The Class AuthorityResource.
109 @SuppressWarnings({"rawtypes", "unchecked"})
110 @Consumes("application/xml")
111 @Produces("application/xml")
112 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
113 extends NuxeoBasedResource {
115 final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
117 final static String SEARCH_TYPE_TERMSTATUS = "ts";
118 public final static String hierarchy = "hierarchy";
120 protected Class<AuthCommon> authCommonClass;
121 protected Class<?> resourceClass;
122 protected String authorityCommonSchemaName;
123 protected String authorityItemCommonSchemaName;
124 final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed? I see no references to it.
126 final static String FETCH_SHORT_ID = "_fetch_";
127 public final static String PARENT_WILDCARD = "_ALL_";
128 protected static final boolean DONT_INCLUDE_ITEMS = false;
129 protected static final boolean INCLUDE_ITEMS = true;
132 * Instantiates a new Authority resource.
134 public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
135 String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
136 this.authCommonClass = authCommonClass;
137 this.resourceClass = resourceClass;
138 this.authorityCommonSchemaName = authorityCommonSchemaName;
139 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
142 public abstract String getItemServiceName();
144 public abstract String getItemTermInfoGroupXPathBase();
147 protected String getVersionString() {
148 return "$LastChangedRevision: 2617 $";
152 public Class<AuthCommon> getCommonPartClass() {
153 return authCommonClass;
157 * Creates the item document handler.
160 * @param inAuthority the in vocabulary
162 * @return the document handler
164 * @throws Exception the exception
166 protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
167 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
168 String inAuthority, String containerShortIdentifier)
170 String authorityRefNameBase;
171 AuthorityItemDocumentModelHandler<?> docHandler;
173 if (containerShortIdentifier == null) {
174 authorityRefNameBase = null;
176 ServiceContext<PoxPayloadIn, PoxPayloadOut> containerCtx = createServiceContext(getServiceName());
177 if (containerShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
178 if (ctx.getCurrentRepositorySession() != null) {
179 containerCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
181 // Get from parent document
182 containerShortIdentifier = getAuthShortIdentifier(containerCtx, inAuthority);
184 authorityRefNameBase = buildAuthorityRefNameBase(containerCtx, containerShortIdentifier);
187 docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
188 ctx.getCommonPartLabel(getItemServiceName()),
190 // FIXME - Richard and Aron think the following three lines should
191 // be in the constructor for the AuthorityItemDocumentModelHandler
192 // because all three are required fields.
193 docHandler.setInAuthority(inAuthority);
194 docHandler.setAuthorityRefNameBase(authorityRefNameBase);
195 docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
199 public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
200 throws DocumentNotFoundException, DocumentException {
201 String shortIdentifier = null;
204 AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
205 shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
206 } catch (Exception e) {
207 if (logger.isDebugEnabled()) {
208 logger.debug("Caught exception ", e);
210 throw new DocumentException(e);
213 return shortIdentifier;
216 protected String buildAuthorityRefNameBase(
217 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
218 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
219 ctx.getServiceName(),
220 null, // Only use shortId form!!!
221 shortIdentifier, null);
222 return authority.toString();
225 public static class CsidAndShortIdentifier {
227 String shortIdentifier;
230 protected String lookupParentCSID(String parentspecifier, String method,
231 String op, UriInfo uriInfo) throws Exception {
232 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(NULL_CONTEXT,
233 parentspecifier, method, op, uriInfo);
234 return tempResult.CSID;
237 protected String lookupParentCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentspecifier, String method,
238 String op, UriInfo uriInfo) throws Exception {
239 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
240 parentspecifier, method, op, uriInfo);
241 return tempResult.CSID;
245 private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
246 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx, // Ok to be null
247 String parentIdentifier,
252 CsidAndShortIdentifier result = new CsidAndShortIdentifier();
253 Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
256 String parentShortIdentifier;
257 if (parentSpec.form == SpecifierForm.CSID) {
258 parentShortIdentifier = null;
259 parentcsid = parentSpec.value;
260 // Uncomment when app layer is ready to integrate
261 // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
262 parentShortIdentifier = FETCH_SHORT_ID;
264 parentShortIdentifier = parentSpec.value;
265 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
266 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
267 CoreSessionInterface repoSession = null;
268 if (existingCtx != null) {
269 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession(); // We want to use the thread's current repo session
271 parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
274 result.CSID = parentcsid;
275 result.shortIdentifier = parentShortIdentifier;
280 public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemspecifier, String parentcsid, String method, String op)
284 Specifier itemSpec = Specifier.getSpecifier(itemspecifier, method, op);
285 if (itemSpec.form == SpecifierForm.CSID) {
286 itemcsid = itemSpec.value;
288 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
289 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
290 CoreSessionInterface repoSession = null;
291 if (existingContext != null) {
292 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession(); // We want to use the thread's current repo session
294 itemcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
301 * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then
302 * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
303 * Resource. They then call this method on that resource.
306 public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item)
307 throws Exception, DocumentNotFoundException {
311 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
312 // Ensure we have the right context.
313 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
315 // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
316 NuxeoRepositoryClientImpl client = (NuxeoRepositoryClientImpl)getRepositoryClient(ctx);
317 String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
319 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
320 ctx = createServiceContext(getItemServiceName());
321 DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
322 DocumentModel docModel = docWrapper.getWrappedObject();
328 public Response createAuthority(
329 @Context ResourceMap resourceMap,
330 @Context UriInfo uriInfo,
333 // 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
334 // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
335 // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
336 // the code that creates new authorities. The authority document model handler will first check for authorities with the same short id before
337 // trying to create a new authority.
339 synchronized(AuthorityResource.class) {
341 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
342 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
343 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
345 String csid = getRepositoryClient(ctx).create(ctx, handler);
346 UriBuilder path = UriBuilder.fromResource(resourceClass);
347 path.path("" + csid);
348 Response response = Response.created(path.build()).build();
350 } catch (Exception e) {
351 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
356 protected boolean supportsReplicating(String tenantId, String serviceName) {
357 boolean result = false;
359 ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
360 result = sb.isSupportsReplicating();
366 * Synchronizes the authority and its items/terms with a Shared Authority Server.
368 * @param specifier either a CSID or one of the urn forms
370 * @return the authority
374 public byte[] synchronize(
375 @Context Request request,
376 @Context UriInfo uriInfo,
377 @PathParam("csid") String identifier) {
378 uriInfo = new UriInfoWrapper(uriInfo);
380 boolean neededSync = false;
381 PoxPayloadOut payloadOut = null;
385 // Prevent multiple SAS synchronizations from occurring simultaneously by synchronizing this method.
387 synchronized(AuthorityResource.class) {
389 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
391 * Make sure this authority service supports synchronization
393 if (supportsReplicating(ctx.getTenantId(), ctx.getServiceName()) == false) {
394 throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
396 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
397 specifier = Specifier.getSpecifier(identifier, "getAuthority", "GET");
398 handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
399 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
400 payloadOut = ctx.getOutput();
401 } catch (Exception e) {
402 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, identifier);
406 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
408 if (neededSync == true) {
409 result = payloadOut.getBytes();
411 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
412 specifier.value).getBytes();
413 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
414 throw new CSWebApplicationException(response);
422 * Builds a cached JAX-RS response.
424 protected Response buildResponse(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, PoxPayloadOut payloadOut) {
425 Response result = null;
427 ResponseBuilder responseBuilder = Response.ok(payloadOut.getBytes());
428 this.setCacheControl(ctx, responseBuilder);
429 result = responseBuilder.build();
435 * Gets the authority.
437 * @param specifier either a CSID or one of the urn forms
439 * @return the authority
445 @Context Request request,
446 @Context UriInfo uriInfo,
447 @PathParam("csid") String specifier) {
448 Response result = null;
449 uriInfo = new UriInfoWrapper(uriInfo);
452 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(request, uriInfo);
453 PoxPayloadOut payloadout = getAuthority(ctx, request, uriInfo, specifier, DONT_INCLUDE_ITEMS);
454 result = buildResponse(ctx, payloadout);
455 } catch (Exception e) {
456 throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
459 if (result == null) {
460 Response response = Response.status(Response.Status.NOT_FOUND).entity(
461 "GET request failed. The requested Authority specifier:" + specifier + ": was not found.").type(
462 "text/plain").build();
463 throw new CSWebApplicationException(response);
469 protected PoxPayloadOut getAuthority(
470 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
474 boolean includeItems) throws Exception {
475 uriInfo = new UriInfoWrapper(uriInfo);
476 PoxPayloadOut payloadout = null;
478 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> docHandler = createDocumentHandler(ctx);
479 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
480 if (spec.form == SpecifierForm.CSID) {
481 if (logger.isDebugEnabled()) {
482 logger.debug("getAuthority with csid=" + spec.value);
484 getRepositoryClient(ctx).get(ctx, spec.value, docHandler);
486 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
487 DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
488 docHandler.setDocumentFilter(myFilter);
489 getRepositoryClient(ctx).get(ctx, docHandler);
492 payloadout = ctx.getOutput();
493 if (includeItems == true) {
494 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
495 payloadout.addPart(PoxPayload.ABSTRACT_COMMON_LIST_ROOT_ELEMENT_LABEL, itemsList);
502 * Finds and populates the authority list.
506 * @return the authority list
509 @Produces("application/xml")
510 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.
511 uriInfo = new UriInfoWrapper(uriInfo);
512 AbstractCommonList result = null;
515 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
516 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
518 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
519 DocumentFilter myFilter = handler.getDocumentFilter();
520 // Need to make the default sort order for authority items
521 // be on the displayName field
522 String sortBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
523 if (sortBy == null || sortBy.isEmpty()) {
524 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
525 + AuthorityItemJAXBSchema.DISPLAY_NAME;
526 myFilter.setOrderByClause(qualifiedDisplayNameField);
528 String nameQ = queryParams.getFirst("refName");
530 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
532 getRepositoryClient(ctx).getFiltered(ctx, handler);
533 result = handler.getCommonPartList();
534 } catch (Exception e) {
535 throw bigReThrow(e, ServiceMessages.GET_FAILED);
542 * Overriding this methods to see if we should update the revision number during the update. We don't
543 * want to update the rev number of synchronization operations.
546 protected PoxPayloadOut update(String csid,
547 PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
548 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
550 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
551 Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
552 if (shouldUpdateRev != null) {
553 handler.setShouldUpdateRevNumber(shouldUpdateRev);
555 getRepositoryClient(ctx).update(ctx, csid, handler);
556 return ctx.getOutput();
562 * @param specifier the csid or id
564 * @return the multipart output
568 public byte[] updateAuthority(
569 @Context ResourceMap resourceMap,
570 @Context UriInfo uriInfo,
571 @PathParam("csid") String specifier,
573 PoxPayloadOut result = null;
575 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
576 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
577 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
578 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
580 if (spec.form == SpecifierForm.CSID) {
583 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
584 csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
586 getRepositoryClient(ctx).update(ctx, csid, handler);
587 result = ctx.getOutput();
588 } catch (Exception e) {
589 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
591 return result.getBytes();
595 * Delete all the items in an authority list.
602 @Path("{csid}/items")
603 public Response deleteAuthorityItemList(@PathParam("csid") String specifier,
604 @Context UriInfo uriInfo) {
605 uriInfo = new UriInfoWrapper(uriInfo);
608 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
609 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
611 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
613 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
615 // Delete all the items one by one
617 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
618 for (ListItem item : itemsList.getListItem()) {
619 deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
621 } catch (Throwable t) {
622 repoSession.setTransactionRollbackOnly();
625 repoClient.releaseRepositorySession(ctx, repoSession);
628 return Response.status(HttpResponseCodes.SC_OK).build();
629 } catch (Exception e) {
630 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
637 * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
639 * @return the response
643 public Response deleteAuthority( // # Delete this authority and all of it's items.
644 @Context Request request,
645 @Context UriInfo uriInfo,
646 @PathParam("csid") String specifier) {
647 uriInfo = new UriInfoWrapper(uriInfo);
649 if (logger.isDebugEnabled()) {
650 logger.debug("deleteAuthority with specifier=" + specifier);
654 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
655 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
656 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
658 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
660 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
662 // First try to delete all the items
664 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
665 for (ListItem item : itemsList.getListItem()) {
666 deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
670 // Lastly, delete the parent/container
672 if (spec.form == SpecifierForm.CSID) {
673 if (logger.isDebugEnabled()) {
674 logger.debug("deleteAuthority with csid=" + spec.value);
676 ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
677 getRepositoryClient(ctx).delete(ctx, spec.value, handler);
679 if (logger.isDebugEnabled()) {
680 logger.debug("deleteAuthority with specifier=" + spec.value);
682 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
683 getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
685 } catch (Throwable t) {
686 repoSession.setTransactionRollbackOnly();
689 repoClient.releaseRepositorySession(ctx, repoSession);
692 return Response.status(HttpResponseCodes.SC_OK).build();
693 } catch (Exception e) {
694 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
698 private String getCsid(ListItem item) {
699 String result = null;
701 for (Element ele : item.getAny()) {
702 String elementName = ele.getTagName().toLowerCase();
703 if (elementName.equals("csid")) {
704 result = ele.getTextContent();
715 * @param parentspecifier - ID of the container. Can be URN or CSID form
716 * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
717 * @param isProposed - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
721 protected Response createAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentIdentifier,
722 boolean shouldUpdateRevNumber,
724 boolean isSasItem) throws Exception {
725 Response result = null;
727 // Note: must have the parentShortId, to do the create.
728 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
729 AuthorityItemDocumentModelHandler handler =
730 (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
731 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
732 handler.setIsProposed(isProposed);
733 handler.setIsSASItem(isSasItem);
734 // Make the client call
735 String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
737 // Build the JAX-RS response
738 UriBuilder path = UriBuilder.fromResource(resourceClass);
739 path.path(parent.CSID + "/items/" + itemcsid);
740 result = Response.created(path.build()).build();
745 public PoxPayloadOut updateAuthorityItem(
746 ServiceContext<PoxPayloadIn, PoxPayloadOut> itemServiceCtx, // Ok to be null. Will be null on PUT calls, but not on sync calls
747 ResourceMap resourceMap,
749 String parentspecifier,
750 String itemspecifier,
751 PoxPayloadIn theUpdate,
752 boolean shouldUpdateRevNumber,
756 PoxPayloadOut result = null;
758 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
759 String parentcsid = csidAndShortId.CSID;
760 String parentShortId = csidAndShortId.shortIdentifier;
762 // If the itemServiceCtx context is not null, use it. Otherwise, create a new context
764 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
766 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
768 ctx.setInput(theUpdate); // the update payload
771 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
773 // We omit the parentShortId, only needed when doing a create...
774 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
775 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
776 if (isProposed != null) {
777 handler.setIsProposed(isProposed);
779 if (isSASItem != null) {
780 handler.setIsSASItem(isSASItem);
782 getRepositoryClient(ctx).update(ctx, itemcsid, handler);
783 result = ctx.getOutput();
789 * Called with an existing context.
791 * @param parentIdentifier
796 public Response createAuthorityItemWithParentContext(ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx,
797 String parentIdentifier,
799 boolean shouldUpdateRevNumber,
801 boolean isSASItem) throws Exception {
802 Response result = null;
804 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
805 parentCtx.getResourceMap(), parentCtx.getUriInfo());
806 if (parentCtx.getCurrentRepositorySession() != null) {
807 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
809 result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
814 /*************************************************************************
815 * Create an AuthorityItem - this is a sub-resource of Authority
816 * @param specifier either a CSID or one of the urn forms
817 * @return Authority item response
818 *************************************************************************/
820 @Path("{csid}/items")
821 public Response createAuthorityItem(
822 @Context ResourceMap resourceMap,
823 @Context UriInfo uriInfo,
824 @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
826 uriInfo = new UriInfoWrapper(uriInfo);
827 Response result = null;
830 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
831 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
832 result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
833 AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
834 } catch (Exception e) {
835 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
842 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
843 public byte[] getItemWorkflow(
844 @PathParam("csid") String csid,
845 @PathParam("itemcsid") String itemcsid) {
846 PoxPayloadOut result = null;
849 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
850 String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
852 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
853 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
854 ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
855 getRepositoryClient(ctx).get(ctx, itemcsid, handler);
856 result = ctx.getOutput();
857 } catch (Exception e) {
858 throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
860 return result.getBytes();
863 //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
864 // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
866 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
867 public byte[] updateItemWorkflowWithTransition(
868 @Context UriInfo uriInfo,
869 @PathParam("csid") String parentIdentifier,
870 @PathParam("itemcsid") String itemIdentifier,
871 @PathParam("transition") String transition) {
872 uriInfo = new UriInfoWrapper(uriInfo);
873 PoxPayloadOut result = null;
876 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
877 result = updateItemWorkflowWithTransition(ctx,
878 parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
879 } catch (Exception e) {
880 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
883 return result.getBytes();
887 * Update an authority item's workflow state.
888 * @param existingContext
893 * @throws DocumentReferenceException
895 public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
896 String parentIdentifier,
897 String itemIdentifier,
899 boolean updateRevNumber) throws DocumentReferenceException {
900 PoxPayloadOut result = null;
904 // We need CSIDs for both the parent authority and the authority item
906 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
907 String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
910 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
912 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(),
913 WorkflowClient.SERVICE_COMMONPART_NAME);
914 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
915 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
916 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
919 // Create a service context and document handler for the target resource -not the workflow resource itself.
921 ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
922 AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
923 targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
924 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
926 // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
928 String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
929 ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
931 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
932 TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
933 if (transitionDef == null) {
934 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
935 itemIdentifier, transition));
937 ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
939 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
940 getRepositoryClient(ctx).update(ctx, itemCsid, handler);
941 result = ctx.getOutput();
942 } catch (DocumentReferenceException de) {
944 } catch (Exception e) {
945 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
951 private PoxPayloadOut getAuthorityItem(
952 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
953 String parentIdentifier,
954 String itemIdentifier) throws Exception {
955 PoxPayloadOut result = null;
957 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
958 // We omit the parentShortId, only needed when doing a create...
959 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
961 Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
962 if (itemSpec.form == SpecifierForm.CSID) {
963 // TODO should we assert that the item is in the passed vocab?
964 getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
966 String itemWhereClause =
967 RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
968 DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
969 handler.setDocumentFilter(myFilter);
970 getRepositoryClient(ctx).get(ctx, handler);
973 result = (PoxPayloadOut) ctx.getOutput();
974 if (result != null) {
975 String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
976 if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
977 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
978 itemSpec.value, inAuthority, parentcsid));
985 public PoxPayloadOut getAuthorityItemWithExistingContext(
986 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
987 String parentIdentifier,
988 String itemIdentifier) throws Exception {
989 PoxPayloadOut result = null;
991 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
992 if (existingCtx.getCurrentRepositorySession() != null) {
993 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
994 ctx.setProperties(existingCtx.getProperties());
996 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1002 * Gets the authority item.
1004 * @param parentspecifier either a CSID or one of the urn forms
1005 * @param itemspecifier either a CSID or one of the urn forms
1007 * @return the authority item
1010 @Path("{csid}/items/{itemcsid}")
1011 public byte[] getAuthorityItem(
1012 @Context Request request,
1013 @Context UriInfo uriInfo,
1014 @Context ResourceMap resourceMap,
1015 @PathParam("csid") String parentIdentifier,
1016 @PathParam("itemcsid") String itemIdentifier) {
1017 uriInfo = new UriInfoWrapper(uriInfo);
1018 PoxPayloadOut result = null;
1020 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx =
1021 (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1023 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1024 ctx.setJaxRsContext(jaxRsContext);
1026 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1027 } catch (DocumentNotFoundException dnf) {
1028 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1029 } catch (Exception e) {
1030 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1033 return result.getBytes();
1037 * Most of the authority child classes will/should use this implementation. However, the Vocabulary service's item schema is
1038 * different enough that it will have to override this method in it's resource class.
1041 protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1042 String result = null;
1044 result = NuxeoUtils.getPrimaryElPathPropertyName(
1045 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1046 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1052 protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1053 String result = null;
1055 result = NuxeoUtils.getMultiElPathPropertyName(
1056 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1057 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1063 * Gets the authorityItem list for the specified authority
1064 * If partialPerm is specified, keywords will be ignored.
1066 * @param authorityIdentifier either a CSID or one of the urn forms
1067 * @param partialTerm if non-null, matches partial terms
1068 * @param keywords if non-null, matches terms in the keyword index for items
1069 * @param ui passed to include additional parameters, like pagination controls
1072 public AbstractCommonList getAuthorityItemList(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1073 String authorityIdentifier,
1074 UriInfo uriInfo) throws Exception {
1075 AbstractCommonList result = null;
1077 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1078 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1079 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
1080 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
1081 ctx.setProperties(existingContext.getProperties());
1084 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
1085 String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
1086 String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
1087 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
1088 String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
1090 // For the wildcard case, parentcsid is null, but docHandler will deal with this.
1091 // We omit the parentShortId, only needed when doing a create...
1092 String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
1093 lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
1094 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
1095 createItemDocumentHandler(ctx, parentcsid, null);
1097 DocumentFilter myFilter = handler.getDocumentFilter();
1098 // If we are not wildcarding the parent, add a restriction
1099 if (parentcsid != null) {
1100 myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
1101 + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
1102 + "'" + parentcsid + "'",
1103 IQueryManager.SEARCH_QUALIFIER_AND);
1106 if (Tools.notBlank(termStatus)) {
1107 // Start with the qualified termStatus field
1108 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
1109 + AuthorityItemJAXBSchema.TERM_STATUS;
1110 String[] filterTerms = termStatus.trim().split("\\|");
1111 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
1112 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
1115 result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
1121 * Gets the authorityItem list for the specified authority
1122 * If partialPerm is specified, keywords will be ignored.
1124 * @param authorityIdentifier either a CSID or one of the urn forms
1125 * @param partialTerm if non-null, matches partial terms
1126 * @param keywords if non-null, matches terms in the keyword index for items
1127 * @param ui passed to include additional parameters, like pagination controls
1129 * @return the authorityItem list
1132 @Path("{csid}/items")
1133 @Produces("application/xml")
1134 public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
1135 @Context UriInfo uriInfo) {
1136 uriInfo = new UriInfoWrapper(uriInfo);
1137 AbstractCommonList result = null;
1140 result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);
1141 } catch (Exception e) {
1142 throw bigReThrow(e, ServiceMessages.LIST_FAILED);
1149 * @return the name of the property used to specify references for items in this type of
1150 * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
1151 * Some types (like Vocabulary) use a separate property.
1153 protected String getRefPropName() {
1154 return ServiceBindingUtils.AUTH_REF_PROP;
1158 * Gets the entities referencing this Authority item instance. The service type
1159 * can be passed as a query param "type", and must match a configured type
1160 * for the service bindings. If not set, the type defaults to
1161 * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1163 * @param parentspecifier either a CSID or one of the urn forms
1164 * @param itemspecifier either a CSID or one of the urn forms
1167 * @return the info for the referencing objects
1170 @Path("{csid}/items/{itemcsid}/refObjs")
1171 @Produces("application/xml")
1172 public AuthorityRefDocList getReferencingObjects(
1173 @PathParam("csid") String parentSpecifier,
1174 @PathParam("itemcsid") String itemSpecifier,
1175 @Context UriTemplateRegistry uriTemplateRegistry,
1176 @Context UriInfo uriInfo) {
1177 uriInfo = new UriInfoWrapper(uriInfo);
1178 AuthorityRefDocList authRefDocList = null;
1180 authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriTemplateRegistry, uriInfo);
1181 } catch (Exception e) {
1182 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1185 if (authRefDocList == null) {
1186 Response response = Response.status(Response.Status.NOT_FOUND).entity(
1187 "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1188 "text/plain").build();
1189 throw new CSWebApplicationException(response);
1191 return authRefDocList;
1194 public AuthorityRefDocList getReferencingObjects(
1195 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1196 String parentspecifier,
1197 String itemspecifier,
1198 UriTemplateRegistry uriTemplateRegistry,
1199 UriInfo uriInfo) throws Exception {
1200 //uriInfo = new UriInfoWrapper(uriInfo);
1201 AuthorityRefDocList authRefDocList = null;
1203 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1204 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1206 // Merge parts of existing context with our new context
1208 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1209 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If one exists, use the existing repo session
1210 ctx.setProperties(existingContext.getProperties());
1213 String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1214 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1216 // Remove the "type" property from the query params
1217 List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1218 if (serviceTypes == null || serviceTypes.isEmpty()) {
1219 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1222 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1223 authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
1225 return authRefDocList;
1229 * Gets the authority terms used in the indicated Authority item.
1231 * @param parentspecifier either a CSID or one of the urn forms
1232 * @param itemspecifier either a CSID or one of the urn forms
1233 * @param ui passed to include additional parameters, like pagination controls
1235 * @return the authority refs for the Authority item.
1238 @Path("{csid}/items/{itemcsid}/authorityrefs")
1239 @Produces("application/xml")
1240 public AuthorityRefList getAuthorityItemAuthorityRefs(
1241 @PathParam("csid") String parentspecifier,
1242 @PathParam("itemcsid") String itemspecifier,
1243 @Context UriInfo uriInfo) {
1244 uriInfo = new UriInfoWrapper(uriInfo);
1245 AuthorityRefList authRefList = null;
1248 // Note that we have to create the service context for the Items, not the main service
1249 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1250 String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1251 // We omit the parentShortId, only needed when doing a create...
1252 DocumentModelHandler<?, AbstractCommonList> handler =
1253 (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1255 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1257 List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1258 authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1259 } catch (Exception e) {
1260 throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1267 * Synchronizes a local authority item with a share authority server (SAS) item.
1269 * @param parentIdentifier
1270 * @param itemIdentifier
1274 private PoxPayloadOut synchronizeItem(
1275 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1276 String parentIdentifier,
1277 String itemIdentifier,
1278 boolean syncHierarchicalRelationships) throws Exception {
1279 PoxPayloadOut result = null;
1280 AuthorityItemSpecifier specifier;
1281 boolean neededSync = false;
1283 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1284 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1285 handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1286 handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1287 handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1288 // Create an authority item specifier
1289 Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1290 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1291 specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1293 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1294 if (neededSync == true) {
1295 result = (PoxPayloadOut) ctx.getOutput();
1302 * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1303 * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1304 * local authority. The parent context was created for the authority (parent) because the sync started there.
1305 * @param existingCtx
1306 * @param parentIdentifier
1307 * @param itemIdentifier
1311 public PoxPayloadOut synchronizeItemWithExistingContext(
1312 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1313 String parentIdentifier,
1314 String itemIdentifier,
1315 boolean syncHierarchicalRelationships
1316 ) throws Exception {
1317 PoxPayloadOut result = null;
1319 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1320 existingCtx.getResourceMap(),
1321 existingCtx.getUriInfo());
1322 if (existingCtx.getCurrentRepositorySession() != null) {
1323 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1326 result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1332 * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1334 * @param specifier either CSIDs and/or one of the urn forms
1336 * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1339 @Path("{csid}/items/{itemcsid}/sync")
1340 public byte[] synchronizeItem(
1341 @Context ResourceMap resourceMap,
1342 @Context UriInfo uriInfo,
1343 @PathParam("csid") String parentIdentifier,
1344 @PathParam("itemcsid") String itemIdentifier) {
1345 uriInfo = new UriInfoWrapper(uriInfo);
1347 boolean neededSync = false;
1348 PoxPayloadOut payloadOut = null;
1351 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1352 payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1353 if (payloadOut != null) {
1356 } catch (Exception e) {
1357 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1361 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
1363 if (neededSync == true) {
1364 result = payloadOut.getBytes();
1366 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1367 itemIdentifier).getBytes();
1368 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1369 throw new CSWebApplicationException(response);
1376 * Update authorityItem.
1378 * @param parentspecifier either a CSID or one of the urn forms
1379 * @param itemspecifier either a CSID or one of the urn forms
1381 * @return the multipart output
1384 @Path("{csid}/items/{itemcsid}")
1385 public byte[] updateAuthorityItem(
1386 @Context ResourceMap resourceMap,
1387 @Context UriInfo uriInfo,
1388 @PathParam("csid") String parentSpecifier,
1389 @PathParam("itemcsid") String itemSpecifier,
1390 String xmlPayload) {
1391 uriInfo = new UriInfoWrapper(uriInfo);
1392 PoxPayloadOut result = null;
1395 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1396 result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1397 AuthorityServiceUtils.UPDATE_REV, // passing TRUE so rev num increases, passing
1398 AuthorityServiceUtils.NO_CHANGE, // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1399 AuthorityServiceUtils.NO_CHANGE); // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1400 } catch (Exception e) {
1401 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1404 return result.getBytes();
1410 * Delete authorityItem.
1412 * @param parentIdentifier the parentcsid
1413 * @param itemIdentifier the itemcsid
1415 * @return the response
1418 @Path("{csid}/items/{itemcsid}")
1419 public Response deleteAuthorityItem(
1420 @Context UriInfo uriInfo,
1421 @PathParam("csid") String parentIdentifier,
1422 @PathParam("itemcsid") String itemIdentifier) {
1423 uriInfo = new UriInfoWrapper(uriInfo);
1424 Response result = null;
1426 ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1427 ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1428 if (logger.isDebugEnabled()) {
1429 logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1433 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1434 deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1435 result = Response.status(HttpResponseCodes.SC_OK).build();
1436 } catch (Exception e) {
1437 throw bigReThrow(e, ServiceMessages.DELETE_FAILED + " itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1445 * @param existingCtx
1446 * @param parentIdentifier
1447 * @param itemIdentifier
1450 public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1451 String parentIdentifier,
1452 String itemIdentifier,
1453 boolean shouldUpdateRevNumber
1454 ) throws Exception {
1455 boolean result = true;
1457 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1458 if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1459 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1460 ctx.setProperties(existingCtx.getProperties());
1463 String parentcsid = null;
1465 parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1466 } catch (DocumentNotFoundException de) {
1467 String msg = String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1468 parentIdentifier, itemIdentifier);
1472 String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1474 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1475 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1476 result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1482 @Path("{csid}/items/{itemcsid}/" + hierarchy)
1483 @Produces("application/xml")
1484 public String getHierarchy(
1485 @PathParam("csid") String parentIdentifier,
1486 @PathParam("itemcsid") String itemIdentifier,
1487 @Context UriInfo uriInfo) throws Exception {
1488 uriInfo = new UriInfoWrapper(uriInfo);
1489 String result = null;
1493 // 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...?
1495 String calledUri = uriInfo.getPath();
1496 String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1497 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1499 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1500 String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1502 String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1503 if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1504 result = Hierarchy.surface(ctx, itemcsid, uri);
1506 result = Hierarchy.dive(ctx, itemcsid, uri);
1508 } catch (Exception e) {
1509 throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1520 public String getItemDocType(String tenantId) {
1521 return getDocType(tenantId, getItemServiceName());
1525 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1526 * for the current resource, for all tenants
1528 * @return a map of URI templates for the current resource, for all tenants
1531 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1532 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1533 super.getUriRegistryEntries();
1534 List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1535 for (String tenantId : tenantIds) {
1536 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1538 return uriRegistryEntriesMap;
1545 public ServiceDescription getDescription(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1546 ServiceDescription result = super.getDescription(ctx);
1547 result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));
1551 public Response createAuthority(String xmlPayload) {
1552 return this.createAuthority(null, null, xmlPayload);