2 * This document is a part of the source code and related artifacts
3 * for CollectionSpace, an open source collections management system
4 * for museums and related institutions:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
24 package org.collectionspace.services.common.vocabulary;
26 import java.util.List;
29 import javax.ws.rs.Consumes;
30 import javax.ws.rs.DELETE;
31 import javax.ws.rs.GET;
32 import javax.ws.rs.POST;
33 import javax.ws.rs.PUT;
34 import javax.ws.rs.Path;
35 import javax.ws.rs.PathParam;
36 import javax.ws.rs.Produces;
37 import javax.ws.rs.core.Context;
38 import javax.ws.rs.core.MultivaluedMap;
39 import javax.ws.rs.core.Request;
40 import javax.ws.rs.core.Response;
41 import javax.ws.rs.core.UriBuilder;
42 import javax.ws.rs.core.UriInfo;
43 import javax.ws.rs.core.Response.ResponseBuilder;
45 import org.collectionspace.services.client.IClientQueryParams;
46 import org.collectionspace.services.client.IQueryManager;
47 import org.collectionspace.services.client.PoxPayload;
48 import org.collectionspace.services.client.PoxPayloadIn;
49 import org.collectionspace.services.client.PoxPayloadOut;
50 import org.collectionspace.services.client.XmlTools;
51 import org.collectionspace.services.client.workflow.WorkflowClient;
53 import org.collectionspace.services.common.CSWebApplicationException;
54 import org.collectionspace.services.common.NuxeoBasedResource;
55 import org.collectionspace.services.common.ResourceMap;
56 import org.collectionspace.services.common.ServiceMain;
57 import org.collectionspace.services.common.ServiceMessages;
58 import org.collectionspace.services.common.StoredValuesUriTemplate;
59 import org.collectionspace.services.common.UriInfoWrapper;
60 import org.collectionspace.services.common.UriTemplateFactory;
61 import org.collectionspace.services.common.UriTemplateRegistryKey;
62 import org.collectionspace.services.common.api.RefName;
63 import org.collectionspace.services.common.api.RefNameUtils;
64 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
65 import org.collectionspace.services.common.api.Tools;
66 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
67 import org.collectionspace.services.common.authorityref.AuthorityRefList;
68 import org.collectionspace.services.common.context.JaxRsContext;
69 import org.collectionspace.services.common.context.MultipartServiceContext;
70 import org.collectionspace.services.common.context.RemoteServiceContext;
71 import org.collectionspace.services.common.context.ServiceBindingUtils;
72 import org.collectionspace.services.common.context.ServiceContext;
73 import org.collectionspace.services.common.document.DocumentException;
74 import org.collectionspace.services.common.document.DocumentFilter;
75 import org.collectionspace.services.common.document.DocumentHandler;
76 import org.collectionspace.services.common.document.DocumentNotFoundException;
77 import org.collectionspace.services.common.document.DocumentReferenceException;
78 import org.collectionspace.services.common.document.DocumentWrapper;
79 import org.collectionspace.services.common.document.Hierarchy;
80 import org.collectionspace.services.common.query.QueryManager;
81 import org.collectionspace.services.common.repository.RepositoryClient;
82 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
83 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
84 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
85 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
86 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
88 import org.collectionspace.services.config.ClientType;
89 import org.collectionspace.services.config.service.ServiceBindingType;
90 import org.collectionspace.services.jaxb.AbstractCommonList;
91 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
92 import org.collectionspace.services.lifecycle.TransitionDef;
93 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
94 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
95 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentFilter;
96 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
97 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
98 import org.collectionspace.services.workflow.WorkflowCommon;
99 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
100 import org.collectionspace.services.description.ServiceDescription;
102 import org.jboss.resteasy.util.HttpResponseCodes;
103 import org.nuxeo.ecm.core.api.DocumentModel;
104 import org.nuxeo.ecm.core.api.DocumentModelList;
105 import org.slf4j.Logger;
106 import org.slf4j.LoggerFactory;
107 import org.w3c.dom.Element;
110 * The Class AuthorityResource.
113 @SuppressWarnings({"rawtypes", "unchecked"})
114 @Consumes("application/xml")
115 @Produces("application/xml")
116 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
117 extends NuxeoBasedResource {
119 final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
121 final static String SEARCH_TYPE_TERMSTATUS = "ts";
122 public final static String hierarchy = "hierarchy";
124 private static final Integer PAGE_NUM_FROM_QUERYPARAMS = null;
125 private static final Integer PAGE_SIZE_FROM_QUERYPARAMS = null;
127 protected Class<AuthCommon> authCommonClass;
128 protected Class<?> resourceClass;
129 protected String authorityCommonSchemaName;
130 protected String authorityItemCommonSchemaName;
131 final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed? I see no references to it.
133 final static String FETCH_SHORT_ID = "_fetch_";
134 public final static String PARENT_WILDCARD = "_ALL_";
135 protected static final boolean DONT_INCLUDE_ITEMS = false;
136 protected static final boolean INCLUDE_ITEMS = true;
139 * Instantiates a new Authority resource.
141 public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
142 String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
143 this.authCommonClass = authCommonClass;
144 this.resourceClass = resourceClass;
145 this.authorityCommonSchemaName = authorityCommonSchemaName;
146 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
149 public abstract String getItemServiceName();
151 public abstract String getItemTermInfoGroupXPathBase();
154 protected String getVersionString() {
155 return "$LastChangedRevision: 2617 $";
159 public Class<AuthCommon> getCommonPartClass() {
160 return authCommonClass;
164 * Creates the item document handler.
167 * @param inAuthority the in vocabulary
169 * @return the document handler
171 * @throws Exception the exception
173 protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
174 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
175 String inAuthority, String containerShortIdentifier)
177 String authorityRefNameBase;
178 AuthorityItemDocumentModelHandler<?> docHandler;
180 if (containerShortIdentifier == null) {
181 authorityRefNameBase = null;
183 ServiceContext<PoxPayloadIn, PoxPayloadOut> containerCtx = createServiceContext(getServiceName());
184 if (containerShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
185 if (ctx.getCurrentRepositorySession() != null) {
186 containerCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
188 // Get from parent document
189 containerShortIdentifier = getAuthShortIdentifier(containerCtx, inAuthority);
191 authorityRefNameBase = buildAuthorityRefNameBase(containerCtx, containerShortIdentifier);
194 docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
195 ctx.getCommonPartLabel(getItemServiceName()),
197 // FIXME - Richard and Aron think the following three lines should
198 // be in the constructor for the AuthorityItemDocumentModelHandler
199 // because all three are required fields.
200 docHandler.setInAuthority(inAuthority);
201 docHandler.setAuthorityRefNameBase(authorityRefNameBase);
202 docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
206 public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
207 throws DocumentNotFoundException, DocumentException {
208 String shortIdentifier = null;
211 AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
212 shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
213 } catch (Exception e) {
214 if (logger.isDebugEnabled()) {
215 logger.debug("Caught exception ", e);
217 throw new DocumentException(e);
220 return shortIdentifier;
223 protected String buildAuthorityRefNameBase(
224 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
225 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
226 ctx.getServiceName(),
227 null, // Only use shortId form!!!
228 shortIdentifier, null);
229 return authority.toString();
232 public static class CsidAndShortIdentifier {
234 String shortIdentifier;
237 protected String lookupParentCSID(String parentspecifier, String method,
238 String op, UriInfo uriInfo) throws Exception {
239 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(NULL_CONTEXT,
240 parentspecifier, method, op, uriInfo);
241 return tempResult.CSID;
244 protected String lookupParentCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentspecifier, String method,
245 String op, UriInfo uriInfo) throws Exception {
246 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
247 parentspecifier, method, op, uriInfo);
248 return tempResult.CSID;
252 private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
253 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx, // Ok to be null
254 String parentIdentifier,
259 CsidAndShortIdentifier result = new CsidAndShortIdentifier();
260 Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
263 String parentShortIdentifier;
264 if (parentSpec.form == SpecifierForm.CSID) {
265 parentShortIdentifier = null;
266 parentcsid = parentSpec.value;
267 // Uncomment when app layer is ready to integrate
268 // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
269 parentShortIdentifier = FETCH_SHORT_ID;
271 parentShortIdentifier = parentSpec.value;
272 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
273 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
274 CoreSessionInterface repoSession = null;
275 if (existingCtx != null) {
276 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession(); // We want to use the thread's current repo session
278 parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
281 result.CSID = parentcsid;
282 result.shortIdentifier = parentShortIdentifier;
287 public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemspecifier, String parentcsid, String method, String op)
291 Specifier itemSpec = Specifier.getSpecifier(itemspecifier, method, op);
292 if (itemSpec.form == SpecifierForm.CSID) {
293 itemcsid = itemSpec.value;
295 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
296 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
297 CoreSessionInterface repoSession = null;
298 if (existingContext != null) {
299 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession(); // We want to use the thread's current repo session
301 itemcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
308 * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then
309 * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
310 * Resource. They then call this method on that resource.
313 public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item)
314 throws Exception, DocumentNotFoundException {
318 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
319 // Ensure we have the right context.
320 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
322 // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
323 NuxeoRepositoryClientImpl client = (NuxeoRepositoryClientImpl)getRepositoryClient(ctx);
324 String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
326 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
327 ctx = createServiceContext(getItemServiceName());
328 DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
329 DocumentModel docModel = docWrapper.getWrappedObject();
335 public Response createAuthority(
336 @Context ResourceMap resourceMap,
337 @Context UriInfo uriInfo,
340 // 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
341 // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
342 // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
343 // the code that creates new authorities. The authority document model handler will first check for authorities with the same short id before
344 // trying to create a new authority.
346 synchronized(AuthorityResource.class) {
348 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
349 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
350 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
352 String csid = getRepositoryClient(ctx).create(ctx, handler);
353 UriBuilder path = UriBuilder.fromResource(resourceClass);
354 path.path("" + csid);
355 Response response = Response.created(path.build()).build();
357 } catch (Exception e) {
358 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
363 protected boolean supportsReplicating(String tenantId, String serviceName) {
364 boolean result = false;
366 ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
367 result = sb.isSupportsReplicating();
373 * Synchronizes the authority and its items/terms with a Shared Authority Server.
375 * @param specifier either a CSID or one of the urn forms
377 * @return the authority
381 public byte[] synchronize(
382 @Context Request request,
383 @Context UriInfo uriInfo,
384 @PathParam("csid") String identifier) {
385 uriInfo = new UriInfoWrapper(uriInfo);
387 boolean neededSync = false;
388 PoxPayloadOut payloadOut = null;
392 // Prevent multiple SAS synchronizations from occurring simultaneously by synchronizing this method.
394 synchronized(AuthorityResource.class) {
396 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
398 * Make sure this authority service supports synchronization
400 if (supportsReplicating(ctx.getTenantId(), ctx.getServiceName()) == false) {
401 throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
403 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
404 specifier = Specifier.getSpecifier(identifier, "getAuthority", "GET");
405 handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
406 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
407 payloadOut = ctx.getOutput();
408 } catch (Exception e) {
409 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, identifier);
413 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
415 if (neededSync == true) {
416 result = payloadOut.getBytes();
418 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
419 specifier.value).getBytes();
420 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
421 throw new CSWebApplicationException(response);
429 * Builds a cached JAX-RS response.
431 protected Response buildResponse(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, PoxPayloadOut payloadOut) {
432 Response result = null;
434 ResponseBuilder responseBuilder = Response.ok(payloadOut.getBytes());
435 this.setCacheControl(ctx, responseBuilder);
436 result = responseBuilder.build();
442 * Gets the authority.
444 * @param specifier either a CSID or one of the urn forms
446 * @return the authority
452 @Context Request request,
453 @Context ResourceMap resourceMap,
454 @Context UriInfo uriInfo,
455 @PathParam("csid") String specifier) {
456 Response result = null;
457 uriInfo = new UriInfoWrapper(uriInfo);
458 PoxPayloadOut payloadout = null;
462 // If the specifier is a fully qualified authority term refname, then return the term payload in the response
464 if (RefNameUtils.isTermRefname(specifier)) {
465 AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(specifier);
466 String parentIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name);
467 String itemIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.name);
468 result = this.getAuthorityItemResponse(request, uriInfo, resourceMap, parentIdentifier, itemIdentifier);
470 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(request, uriInfo);
471 payloadout = getAuthority(ctx, request, uriInfo, specifier, DONT_INCLUDE_ITEMS);
472 result = buildResponse(ctx, payloadout);
474 } catch (Exception e) {
475 throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
478 if (result == null) {
479 Response response = Response.status(Response.Status.NOT_FOUND).entity(
480 "GET request failed. The requested Authority specifier:" + specifier + ": was not found.").type(
481 "text/plain").build();
482 throw new CSWebApplicationException(response);
488 protected PoxPayloadOut getAuthority(
489 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
493 boolean includeItems) throws Exception {
494 uriInfo = new UriInfoWrapper(uriInfo);
495 PoxPayloadOut payloadout = null;
497 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> docHandler = createDocumentHandler(ctx);
498 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
499 if (spec.form == SpecifierForm.CSID) {
500 if (logger.isDebugEnabled()) {
501 logger.debug("getAuthority with csid=" + spec.value);
503 getRepositoryClient(ctx).get(ctx, spec.value, docHandler);
505 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
506 DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
507 docHandler.setDocumentFilter(myFilter);
508 getRepositoryClient(ctx).get(ctx, docHandler);
511 payloadout = ctx.getOutput();
512 if (includeItems == true) {
513 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
514 payloadout.addPart(PoxPayload.ABSTRACT_COMMON_LIST_ROOT_ELEMENT_LABEL, itemsList);
521 * Finds and populates the authority list.
525 * @return the authority list
528 @Produces("application/xml")
529 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.
530 uriInfo = new UriInfoWrapper(uriInfo);
531 AbstractCommonList result = null;
534 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
535 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
537 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
538 DocumentFilter myFilter = handler.getDocumentFilter();
539 // Need to make the default sort order for authority items
540 // be on the displayName field
541 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
542 if (orderBy == null || orderBy.isEmpty()) {
543 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
544 + AuthorityItemJAXBSchema.DISPLAY_NAME;
545 myFilter.setOrderByClause(qualifiedDisplayNameField);
547 String nameQ = queryParams.getFirst("refName");
549 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
551 //getRepositoryClient(ctx).getFiltered(ctx, handler); # Something here?
552 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
553 result = search(ctx, handler, uriInfo, orderBy, null, advancedSearch, null);
554 result = handler.getCommonPartList();
555 } catch (Exception e) {
556 throw bigReThrow(e, ServiceMessages.GET_FAILED);
563 * Overriding this methods to see if we should update the revision number during the update. We don't
564 * want to update the rev number of synchronization operations.
567 protected PoxPayloadOut update(String csid,
568 PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
569 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
571 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
572 Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
573 if (shouldUpdateRev != null) {
574 handler.setShouldUpdateRevNumber(shouldUpdateRev);
576 getRepositoryClient(ctx).update(ctx, csid, handler);
577 return ctx.getOutput();
583 * @param specifier the csid or id
585 * @return the multipart output
589 public byte[] updateAuthority(
590 @Context Request request,
591 @Context ResourceMap resourceMap,
592 @Context UriInfo uriInfo,
593 @PathParam("csid") String specifier,
595 PoxPayloadOut result = null;
597 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
598 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
599 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
600 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
601 String csid = getCsid(ctx, spec);
602 getRepositoryClient(ctx).update(ctx, csid, handler);
603 result = ctx.getOutput();
604 } catch (Exception e) {
605 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
607 return result.getBytes();
611 * Delete all the items in an authority list.
618 @Path("{csid}/items")
619 public Response deleteAuthorityItemList(@PathParam("csid") String specifier,
620 @Context UriInfo uriInfo) {
621 uriInfo = new UriInfoWrapper(uriInfo);
624 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
625 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
627 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
629 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
631 // Delete all the items one by one
633 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
634 for (ListItem item : itemsList.getListItem()) {
635 deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
637 } catch (Throwable t) {
638 repoSession.setTransactionRollbackOnly();
641 repoClient.releaseRepositorySession(ctx, repoSession);
644 return Response.status(HttpResponseCodes.SC_OK).build();
645 } catch (Exception e) {
646 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
653 * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
655 * @return the response
659 public Response deleteAuthority( // # Delete this authority and all of it's items.
660 @Context Request request,
661 @Context UriInfo uriInfo,
662 @PathParam("csid") String specifier) {
663 uriInfo = new UriInfoWrapper(uriInfo);
665 if (logger.isDebugEnabled()) {
666 logger.debug("deleteAuthority with specifier=" + specifier);
670 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
671 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
672 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
674 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
676 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
678 // First try to delete all the items
680 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
681 for (ListItem item : itemsList.getListItem()) {
682 deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
686 // Lastly, delete the parent/container
688 if (spec.form == SpecifierForm.CSID) {
689 if (logger.isDebugEnabled()) {
690 logger.debug("deleteAuthority with csid=" + spec.value);
692 ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
693 getRepositoryClient(ctx).delete(ctx, spec.value, handler);
695 if (logger.isDebugEnabled()) {
696 logger.debug("deleteAuthority with specifier=" + spec.value);
698 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
699 getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
701 } catch (Throwable t) {
702 repoSession.setTransactionRollbackOnly();
705 repoClient.releaseRepositorySession(ctx, repoSession);
708 return Response.status(HttpResponseCodes.SC_OK).build();
709 } catch (Exception e) {
710 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
714 protected String getCsid(ListItem item) {
715 String result = null;
717 for (Element ele : item.getAny()) {
718 String elementName = ele.getTagName().toLowerCase();
719 if (elementName.equals("csid")) {
720 result = ele.getTextContent();
731 * @param parentspecifier - ID of the container. Can be URN or CSID form
732 * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
733 * @param isProposed - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
737 protected Response createAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentIdentifier,
738 boolean shouldUpdateRevNumber,
740 boolean isSasItem) throws Exception {
741 Response result = null;
743 // Note: must have the parentShortId, to do the create.
744 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
745 AuthorityItemDocumentModelHandler handler =
746 (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
747 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
748 handler.setIsProposed(isProposed);
749 handler.setIsSASItem(isSasItem);
750 // Make the client call
751 String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
753 // Build the JAX-RS response
754 UriBuilder path = UriBuilder.fromResource(resourceClass);
755 path.path(parent.CSID + "/items/" + itemcsid);
756 result = Response.created(path.build()).build();
761 public PoxPayloadOut updateAuthorityItem(
762 ServiceContext<PoxPayloadIn, PoxPayloadOut> itemServiceCtx, // Ok to be null. Will be null on PUT calls, but not on sync calls
763 ResourceMap resourceMap,
765 String parentspecifier,
766 String itemspecifier,
767 PoxPayloadIn theUpdate,
768 boolean shouldUpdateRevNumber,
772 PoxPayloadOut result = null;
774 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
775 String parentcsid = csidAndShortId.CSID;
776 String parentShortId = csidAndShortId.shortIdentifier;
778 // If the itemServiceCtx context is not null, use it. Otherwise, create a new context
780 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
782 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
784 ctx.setInput(theUpdate); // the update payload
787 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
789 // We omit the parentShortId, only needed when doing a create...
790 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
791 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
793 // Update the SAS fields if either value is non-null
795 boolean updateSASFields = isProposed != null || isSASItem != null;
796 handler.setshouldUpdateSASFields(updateSASFields);
797 if (updateSASFields == true) {
798 handler.setshouldUpdateSASFields(true);
799 if (isProposed != null) {
800 handler.setIsProposed(isProposed);
802 if (isSASItem != null) {
803 handler.setIsSASItem(isSASItem);
807 getRepositoryClient(ctx).update(ctx, itemcsid, handler);
808 result = ctx.getOutput();
814 * Called with an existing context.
816 * @param parentIdentifier
821 public Response createAuthorityItemWithParentContext(ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx,
822 String parentIdentifier,
824 boolean shouldUpdateRevNumber,
826 boolean isSASItem) throws Exception {
827 Response result = null;
829 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
830 parentCtx.getResourceMap(), parentCtx.getUriInfo());
831 if (parentCtx.getCurrentRepositorySession() != null) {
832 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
834 result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
839 /*************************************************************************
840 * Create an AuthorityItem - this is a sub-resource of Authority
841 * @param specifier either a CSID or one of the urn forms
842 * @return Authority item response
843 *************************************************************************/
845 @Path("{csid}/items")
846 public Response createAuthorityItem(
847 @Context ResourceMap resourceMap,
848 @Context UriInfo uriInfo,
849 @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
851 uriInfo = new UriInfoWrapper(uriInfo);
852 Response result = null;
855 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
856 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
857 result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
858 AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
859 } catch (Exception e) {
860 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
867 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
868 public byte[] getItemWorkflow(
869 @PathParam("csid") String csid,
870 @PathParam("itemcsid") String itemcsid) {
871 PoxPayloadOut result = null;
874 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
875 String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
877 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
878 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
879 ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
880 getRepositoryClient(ctx).get(ctx, itemcsid, handler);
881 result = ctx.getOutput();
882 } catch (Exception e) {
883 throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
885 return result.getBytes();
889 * We should consider changing this code. The RepositoryClient (from call to getRepositoryClient) could support a call doWorkflowTransition() instead?
893 @Path("{csid}" + WorkflowClient.SERVICE_PATH + "/" + "{transition}")
894 public byte[] updateWorkflowWithTransition(
895 @Context UriInfo uriInfo,
896 @PathParam("csid") String specifier,
897 @PathParam("transition") String transition) {
898 PoxPayloadOut result = null;
900 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
903 csid = getCsid(null, spec);
904 result = updateWorkflowWithTransition(NULL_CONTEXT, uriInfo, csid, transition);
905 } catch (Exception e) {
906 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
909 return result.getBytes();
912 //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
913 // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
915 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
916 public byte[] updateItemWorkflowWithTransition(
917 @Context UriInfo uriInfo,
918 @PathParam("csid") String parentIdentifier,
919 @PathParam("itemcsid") String itemIdentifier,
920 @PathParam("transition") String transition) {
921 uriInfo = new UriInfoWrapper(uriInfo);
922 PoxPayloadOut result = null;
925 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
926 result = updateItemWorkflowWithTransition(ctx,
927 parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
928 } catch (Exception e) {
929 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
932 return result.getBytes();
936 * Update an authority item's workflow state.
937 * @param existingContext
942 * @throws DocumentReferenceException
944 public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
945 String parentIdentifier,
946 String itemIdentifier,
948 boolean updateRevNumber) throws DocumentReferenceException {
949 PoxPayloadOut result = null;
953 // We need CSIDs for both the parent authority and the authority item
955 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
956 String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
959 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
961 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(),
962 WorkflowClient.SERVICE_COMMONPART_NAME);
963 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
964 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
965 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
968 // Create a service context and document handler for the target resource -not the workflow resource itself.
970 ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
971 AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
972 targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
973 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
975 // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
977 String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
978 ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
980 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
981 TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
982 if (transitionDef == null) {
983 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
984 itemIdentifier, transition));
986 ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
988 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
989 getRepositoryClient(ctx).update(ctx, itemCsid, handler);
990 result = ctx.getOutput();
991 } catch (DocumentReferenceException de) {
993 } catch (Exception e) {
994 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
1000 protected PoxPayloadOut getAuthorityItem(
1001 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1002 String parentIdentifier,
1003 String itemIdentifier) throws Exception {
1004 PoxPayloadOut result = null;
1006 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
1007 // We omit the parentShortId, only needed when doing a create...
1008 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
1010 Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
1011 if (itemSpec.form == SpecifierForm.CSID) {
1012 // TODO should we assert that the item is in the passed vocab?
1013 getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
1015 String itemWhereClause =
1016 RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
1017 DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
1018 handler.setDocumentFilter(myFilter);
1019 getRepositoryClient(ctx).get(ctx, handler);
1022 result = (PoxPayloadOut) ctx.getOutput();
1023 if (result != null) {
1024 String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
1025 if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
1026 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
1027 itemSpec.value, inAuthority, parentcsid));
1034 public PoxPayloadOut getAuthorityItemWithExistingContext(
1035 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1036 String parentIdentifier,
1037 String itemIdentifier) throws Exception {
1038 PoxPayloadOut result = null;
1040 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
1041 if (existingCtx.getCurrentRepositorySession() != null) {
1042 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
1043 ctx.setProperties(existingCtx.getProperties());
1045 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1051 * Gets the authority item.
1053 * @param parentspecifier either a CSID or one of the urn forms
1054 * @param itemspecifier either a CSID or one of the urn forms
1056 * @return the authority item
1059 @Path("{csid}/items/{itemcsid}")
1060 public byte[] getAuthorityItem(
1061 @Context Request request,
1062 @Context UriInfo uriInfo,
1063 @Context ResourceMap resourceMap,
1064 @PathParam("csid") String parentIdentifier,
1065 @PathParam("itemcsid") String itemIdentifier) {
1066 uriInfo = new UriInfoWrapper(uriInfo);
1067 PoxPayloadOut result = null;
1069 result = this.getAuthorityItemPayload(request, uriInfo, resourceMap, parentIdentifier, itemIdentifier);
1071 return result.getBytes();
1075 public PoxPayloadOut getAuthorityItemPayload(
1076 @Context Request request,
1077 @Context UriInfo uriInfo,
1078 @Context ResourceMap resourceMap,
1079 @PathParam("csid") String parentIdentifier,
1080 @PathParam("itemcsid") String itemIdentifier) {
1081 uriInfo = new UriInfoWrapper(uriInfo);
1082 PoxPayloadOut result = null;
1084 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx =
1085 (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1087 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1088 ctx.setJaxRsContext(jaxRsContext);
1090 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1091 } catch (DocumentNotFoundException dnf) {
1092 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1093 } catch (Exception e) {
1094 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1100 public Response getAuthorityItemResponse(
1101 @Context Request request,
1102 @Context UriInfo uriInfo,
1103 @Context ResourceMap resourceMap,
1104 @PathParam("csid") String parentIdentifier,
1105 @PathParam("itemcsid") String itemIdentifier) {
1106 uriInfo = new UriInfoWrapper(uriInfo);
1107 PoxPayloadOut payloadout = null;
1108 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
1111 ctx = (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1113 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1114 ctx.setJaxRsContext(jaxRsContext);
1116 payloadout = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1117 } catch (DocumentNotFoundException dnf) {
1118 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1119 } catch (Exception e) {
1120 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1123 return buildResponse(ctx, payloadout);
1128 * Most of the authority child classes will/should use this implementation. However, the Vocabulary service's item schema is
1129 * different enough that it will have to override this method in it's resource class.
1132 protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1133 String result = null;
1135 result = NuxeoUtils.getPrimaryElPathPropertyName(
1136 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1137 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1143 protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1144 String result = null;
1146 result = NuxeoUtils.getMultiElPathPropertyName(
1147 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1148 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1154 * Gets the authorityItem list for the specified authority
1155 * If partialPerm is specified, keywords will be ignored.
1157 * @param authorityIdentifier either a CSID or one of the urn forms
1158 * @param partialTerm if non-null, matches partial terms
1159 * @param keywords if non-null, matches terms in the keyword index for items
1160 * @param ui passed to include additional parameters, like pagination controls
1163 public AbstractCommonList getAuthorityItemList(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1164 String authorityIdentifier,
1165 UriInfo uriInfo) throws Exception {
1166 AbstractCommonList result = null;
1168 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1169 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1170 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
1171 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
1172 ctx.setProperties(existingContext.getProperties());
1175 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
1176 String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
1177 String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
1178 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
1179 String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
1181 // For the wildcard case, parentcsid is null, but docHandler will deal with this.
1182 // We omit the parentShortId, only needed when doing a create...
1183 String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
1184 lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
1185 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
1186 createItemDocumentHandler(ctx, parentcsid, null);
1188 DocumentFilter myFilter = handler.getDocumentFilter();
1189 // If we are not wildcarding the parent, add a restriction
1190 if (parentcsid != null) {
1191 myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
1192 + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
1193 + "'" + parentcsid + "'",
1194 IQueryManager.SEARCH_QUALIFIER_AND);
1197 if (Tools.notBlank(termStatus)) {
1198 // Start with the qualified termStatus field
1199 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
1200 + AuthorityItemJAXBSchema.TERM_STATUS;
1201 String[] filterTerms = termStatus.trim().split("\\|");
1202 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
1203 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
1206 result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
1212 * Gets the authorityItem list for the specified authority
1213 * If partialPerm is specified, keywords will be ignored.
1215 * @param authorityIdentifier either a CSID or one of the urn forms
1216 * @param partialTerm if non-null, matches partial terms
1217 * @param keywords if non-null, matches terms in the keyword index for items
1218 * @param ui passed to include additional parameters, like pagination controls
1220 * @return the authorityItem list
1223 @Path("{csid}/items")
1224 @Produces("application/xml")
1225 public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
1226 @Context UriInfo uriInfo) {
1227 uriInfo = new UriInfoWrapper(uriInfo);
1228 AbstractCommonList result = null;
1231 result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);
1232 } catch (Exception e) {
1233 throw bigReThrow(e, ServiceMessages.LIST_FAILED);
1240 * @return the name of the property used to specify references for items in this type of
1241 * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
1242 * Some types (like Vocabulary) use a separate property.
1244 protected String getRefPropName() {
1245 return ServiceBindingUtils.AUTH_REF_PROP;
1249 * Gets the entities referencing this Authority item instance. The service type
1250 * can be passed as a query param "type", and must match a configured type
1251 * for the service bindings. If not set, the type defaults to
1252 * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1254 * @param parentspecifier either a CSID or one of the urn forms
1255 * @param itemspecifier either a CSID or one of the urn forms
1258 * @return the info for the referencing objects
1261 @Path("{csid}/items/{itemcsid}/refObjs")
1262 @Produces("application/xml")
1263 public AuthorityRefDocList getReferencingObjects(
1264 @PathParam("csid") String parentSpecifier,
1265 @PathParam("itemcsid") String itemSpecifier,
1266 @Context UriInfo uriInfo) {
1267 uriInfo = new UriInfoWrapper(uriInfo);
1268 AuthorityRefDocList authRefDocList = null;
1270 authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriInfo, PAGE_NUM_FROM_QUERYPARAMS, PAGE_SIZE_FROM_QUERYPARAMS, true, true);
1271 } catch (Exception e) {
1272 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1275 if (authRefDocList == null) {
1276 Response response = Response.status(Response.Status.NOT_FOUND).entity(
1277 "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1278 "text/plain").build();
1279 throw new CSWebApplicationException(response);
1281 return authRefDocList;
1284 public AuthorityRefDocList getReferencingObjects(
1285 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1286 String parentspecifier,
1287 String itemspecifier,
1291 boolean useDefaultOrderByClause,
1292 boolean computeTotal) throws Exception {
1293 AuthorityRefDocList authRefDocList = null;
1295 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1296 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1298 // Merge parts of existing context with our new context
1300 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1301 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If one exists, use the existing repo session
1302 ctx.setProperties(existingContext.getProperties());
1305 String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1306 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1308 // Remove the "type" property from the query params
1309 List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1310 if (serviceTypes == null || serviceTypes.isEmpty()) {
1311 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1314 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1315 authRefDocList = handler.getReferencingObjects(ctx, serviceTypes, getRefPropName(), itemcsid, pageNum, pageSize, useDefaultOrderByClause, computeTotal);
1317 return authRefDocList;
1321 * Gets the authority terms used in the indicated Authority item.
1323 * @param parentspecifier either a CSID or one of the urn forms
1324 * @param itemspecifier either a CSID or one of the urn forms
1325 * @param ui passed to include additional parameters, like pagination controls
1327 * @return the authority refs for the Authority item.
1330 @Path("{csid}/items/{itemcsid}/authorityrefs")
1331 @Produces("application/xml")
1332 public AuthorityRefList getAuthorityItemAuthorityRefs(
1333 @PathParam("csid") String parentspecifier,
1334 @PathParam("itemcsid") String itemspecifier,
1335 @Context UriInfo uriInfo) {
1336 uriInfo = new UriInfoWrapper(uriInfo);
1337 AuthorityRefList authRefList = null;
1340 // Note that we have to create the service context for the Items, not the main service
1341 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1342 String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1343 // We omit the parentShortId, only needed when doing a create...
1344 DocumentModelHandler<?, AbstractCommonList> handler =
1345 (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1347 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1349 List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1350 authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1351 } catch (Exception e) {
1352 throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1359 * Synchronizes a local authority item with a share authority server (SAS) item.
1361 * @param parentIdentifier
1362 * @param itemIdentifier
1366 private PoxPayloadOut synchronizeItem(
1367 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1368 String parentIdentifier,
1369 String itemIdentifier,
1370 boolean syncHierarchicalRelationships) throws Exception {
1371 PoxPayloadOut result = null;
1372 AuthorityItemSpecifier specifier;
1373 boolean neededSync = false;
1375 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1376 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1377 handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1378 handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1379 handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1380 // Create an authority item specifier
1381 Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1382 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1383 specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1385 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1386 if (neededSync == true) {
1387 result = (PoxPayloadOut) ctx.getOutput();
1394 * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1395 * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1396 * local authority. The parent context was created for the authority (parent) because the sync started there.
1397 * @param existingCtx
1398 * @param parentIdentifier
1399 * @param itemIdentifier
1403 public PoxPayloadOut synchronizeItemWithExistingContext(
1404 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1405 String parentIdentifier,
1406 String itemIdentifier,
1407 boolean syncHierarchicalRelationships
1408 ) throws Exception {
1409 PoxPayloadOut result = null;
1411 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1412 existingCtx.getResourceMap(),
1413 existingCtx.getUriInfo());
1414 if (existingCtx.getCurrentRepositorySession() != null) {
1415 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1418 result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1424 * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1426 * @param specifier either CSIDs and/or one of the urn forms
1428 * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1431 @Path("{csid}/items/{itemcsid}/sync")
1432 public byte[] synchronizeItem(
1433 @Context ResourceMap resourceMap,
1434 @Context UriInfo uriInfo,
1435 @PathParam("csid") String parentIdentifier,
1436 @PathParam("itemcsid") String itemIdentifier) {
1437 uriInfo = new UriInfoWrapper(uriInfo);
1439 boolean neededSync = false;
1440 PoxPayloadOut payloadOut = null;
1443 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1444 payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1445 if (payloadOut != null) {
1448 } catch (Exception e) {
1449 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1453 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
1455 if (neededSync == true) {
1456 result = payloadOut.getBytes();
1458 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1459 itemIdentifier).getBytes();
1460 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1461 throw new CSWebApplicationException(response);
1468 * Update authorityItem.
1470 * @param parentspecifier either a CSID or one of the urn forms
1471 * @param itemspecifier either a CSID or one of the urn forms
1473 * @return the multipart output
1476 @Path("{csid}/items/{itemcsid}")
1477 public byte[] updateAuthorityItem(
1478 @Context ResourceMap resourceMap,
1479 @Context UriInfo uriInfo,
1480 @PathParam("csid") String parentSpecifier,
1481 @PathParam("itemcsid") String itemSpecifier,
1482 String xmlPayload) {
1483 uriInfo = new UriInfoWrapper(uriInfo);
1484 PoxPayloadOut result = null;
1487 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1488 result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1489 AuthorityServiceUtils.UPDATE_REV, // passing TRUE so rev num increases, passing
1490 AuthorityServiceUtils.NO_CHANGE, // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1491 AuthorityServiceUtils.NO_CHANGE); // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1492 } catch (Exception e) {
1493 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1496 return result.getBytes();
1502 * Delete authorityItem.
1504 * @param parentIdentifier the parentcsid
1505 * @param itemIdentifier the itemcsid
1507 * @return the response
1510 @Path("{csid}/items/{itemcsid}")
1511 public Response deleteAuthorityItem(
1512 @Context UriInfo uriInfo,
1513 @PathParam("csid") String parentIdentifier,
1514 @PathParam("itemcsid") String itemIdentifier) {
1515 uriInfo = new UriInfoWrapper(uriInfo);
1516 Response result = null;
1518 ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1519 ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1520 if (logger.isDebugEnabled()) {
1521 logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1525 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1526 deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1527 result = Response.status(HttpResponseCodes.SC_OK).build();
1528 } catch (Exception e) {
1529 throw bigReThrow(e, ServiceMessages.DELETE_FAILED + " itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1537 * @param existingCtx
1538 * @param parentIdentifier
1539 * @param itemIdentifier
1542 public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1543 String parentIdentifier,
1544 String itemIdentifier,
1545 boolean shouldUpdateRevNumber
1546 ) throws Exception {
1547 boolean result = true;
1549 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1550 if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1551 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1552 ctx.setProperties(existingCtx.getProperties());
1555 String parentcsid = null;
1557 parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1558 } catch (DocumentNotFoundException de) {
1559 String msg = String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1560 parentIdentifier, itemIdentifier);
1564 String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1566 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1567 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1568 result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1574 @Path("{csid}/items/{itemcsid}/" + hierarchy)
1575 @Produces("application/xml")
1576 public String getHierarchy(
1577 @PathParam("csid") String parentIdentifier,
1578 @PathParam("itemcsid") String itemIdentifier,
1579 @Context UriInfo uriInfo) throws Exception {
1580 uriInfo = new UriInfoWrapper(uriInfo);
1581 String result = null;
1585 // 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...?
1587 String calledUri = uriInfo.getPath();
1588 String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1589 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1591 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1592 String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1594 String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1595 if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1596 result = Hierarchy.surface(ctx, itemcsid, uri);
1598 result = Hierarchy.dive(ctx, itemcsid, uri);
1600 } catch (Exception e) {
1601 throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1612 public String getItemDocType(String tenantId) {
1613 return getDocType(tenantId, getItemServiceName());
1617 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1618 * for the current resource, for all tenants
1620 * @return a map of URI templates for the current resource, for all tenants
1623 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1624 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1625 super.getUriRegistryEntries();
1626 List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1627 for (String tenantId : tenantIds) {
1628 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1630 return uriRegistryEntriesMap;
1637 public ServiceDescription getDescription(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1638 ServiceDescription result = super.getDescription(ctx);
1639 result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));
1643 public Response createAuthority(String xmlPayload) {
1644 return this.createAuthority(null, null, xmlPayload);
1647 protected String getCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, Specifier specifier) throws Exception {
1651 ctx = createServiceContext(getServiceName());
1654 if (specifier.form == SpecifierForm.CSID) {
1655 csid = specifier.value;
1657 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, specifier.value);
1658 csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);