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.Response.ResponseBuilder;
42 import javax.ws.rs.core.UriBuilder;
43 import javax.ws.rs.core.UriInfo;
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.UriTemplateRegistryKey;
61 import org.collectionspace.services.common.api.RefName;
62 import org.collectionspace.services.common.api.RefNameUtils;
63 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
64 import org.collectionspace.services.common.api.Tools;
65 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
66 import org.collectionspace.services.common.authorityref.AuthorityRefList;
67 import org.collectionspace.services.common.context.JaxRsContext;
68 import org.collectionspace.services.common.context.MultipartServiceContext;
69 import org.collectionspace.services.common.context.RemoteServiceContext;
70 import org.collectionspace.services.common.context.ServiceBindingUtils;
71 import org.collectionspace.services.common.context.ServiceContext;
72 import org.collectionspace.services.common.document.DocumentException;
73 import org.collectionspace.services.common.document.DocumentFilter;
74 import org.collectionspace.services.common.document.DocumentHandler;
75 import org.collectionspace.services.common.document.DocumentNotFoundException;
76 import org.collectionspace.services.common.document.DocumentReferenceException;
77 import org.collectionspace.services.common.document.DocumentWrapper;
78 import org.collectionspace.services.common.document.Hierarchy;
79 import org.collectionspace.services.common.query.QueryManager;
80 import org.collectionspace.services.common.repository.RepositoryClient;
81 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
82 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
83 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
84 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
85 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
86 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
87 import org.collectionspace.services.config.ClientType;
88 import org.collectionspace.services.config.service.ServiceBindingType;
89 import org.collectionspace.services.description.ServiceDescription;
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.CoreSessionInterface;
94 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
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.jboss.resteasy.util.HttpResponseCodes;
100 import org.nuxeo.ecm.core.api.DocumentModel;
101 import org.nuxeo.ecm.core.api.DocumentModelList;
102 import org.slf4j.Logger;
103 import org.slf4j.LoggerFactory;
104 import org.w3c.dom.Element;
107 * The Class AuthorityResource.
110 @SuppressWarnings({"rawtypes", "unchecked"})
111 @Consumes("application/xml")
112 @Produces("application/xml")
113 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
114 extends NuxeoBasedResource {
116 final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
118 final static String SEARCH_TYPE_TERMSTATUS = "ts";
119 public final static String hierarchy = "hierarchy";
121 private static final Integer PAGE_NUM_FROM_QUERYPARAMS = null;
122 private static final Integer PAGE_SIZE_FROM_QUERYPARAMS = null;
124 protected Class<AuthCommon> authCommonClass;
125 protected Class<?> resourceClass;
126 protected String authorityCommonSchemaName;
127 protected String authorityItemCommonSchemaName;
128 final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed? I see no references to it.
130 final static String FETCH_SHORT_ID = "_fetch_";
131 public final static String PARENT_WILDCARD = "_ALL_";
132 protected static final boolean DONT_INCLUDE_ITEMS = false;
133 protected static final boolean INCLUDE_ITEMS = true;
136 * Instantiates a new Authority resource.
138 public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
139 String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
140 this.authCommonClass = authCommonClass;
141 this.resourceClass = resourceClass;
142 this.authorityCommonSchemaName = authorityCommonSchemaName;
143 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
146 public abstract String getItemServiceName();
148 public abstract String getItemTermInfoGroupXPathBase();
151 protected String getVersionString() {
152 return "$LastChangedRevision: 2617 $";
156 public Class<AuthCommon> getCommonPartClass() {
157 return authCommonClass;
161 * Creates the item document handler.
164 * @param inAuthority the in vocabulary
166 * @return the document handler
168 * @throws Exception the exception
170 protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
171 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
172 String inAuthority, String containerShortIdentifier)
174 String authorityRefNameBase;
175 AuthorityItemDocumentModelHandler<?> docHandler;
177 if (containerShortIdentifier == null) {
178 authorityRefNameBase = null;
180 ServiceContext<PoxPayloadIn, PoxPayloadOut> containerCtx = createServiceContext(getServiceName());
181 if (containerShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
182 if (ctx.getCurrentRepositorySession() != null) {
183 containerCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
185 // Get from parent document
186 containerShortIdentifier = getAuthShortIdentifier(containerCtx, inAuthority);
188 authorityRefNameBase = buildAuthorityRefNameBase(containerCtx, containerShortIdentifier);
191 docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
192 ctx.getCommonPartLabel(getItemServiceName()),
194 // FIXME - Richard and Aron think the following three lines should
195 // be in the constructor for the AuthorityItemDocumentModelHandler
196 // because all three are required fields.
197 docHandler.setInAuthority(inAuthority);
198 docHandler.setAuthorityRefNameBase(authorityRefNameBase);
199 docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
203 public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
204 throws DocumentNotFoundException, DocumentException {
205 String shortIdentifier = null;
208 AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
209 shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
210 } catch (Exception e) {
211 if (logger.isDebugEnabled()) {
212 logger.debug("Caught exception ", e);
214 throw new DocumentException(e);
217 return shortIdentifier;
220 protected String buildAuthorityRefNameBase(
221 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
222 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
223 ctx.getServiceName(),
224 null, // Only use shortId form!!!
225 shortIdentifier, null);
226 return authority.toString();
229 public static class CsidAndShortIdentifier {
231 String shortIdentifier;
234 protected String lookupParentCSID(String parentspecifier, String method,
235 String op, UriInfo uriInfo) throws Exception {
236 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(NULL_CONTEXT,
237 parentspecifier, method, op, uriInfo);
238 return tempResult.CSID;
241 protected String lookupParentCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentspecifier, String method,
242 String op, UriInfo uriInfo) throws Exception {
243 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
244 parentspecifier, method, op, uriInfo);
245 return tempResult.CSID;
249 private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
250 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx, // Ok to be null
251 String parentIdentifier,
256 CsidAndShortIdentifier result = new CsidAndShortIdentifier();
257 Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
260 String parentShortIdentifier;
261 if (parentSpec.form == SpecifierForm.CSID) {
262 parentShortIdentifier = null;
263 parentcsid = parentSpec.value;
264 // Uncomment when app layer is ready to integrate
265 // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
266 parentShortIdentifier = FETCH_SHORT_ID;
268 parentShortIdentifier = parentSpec.value;
269 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
270 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
271 CoreSessionInterface repoSession = null;
272 if (existingCtx != null) {
273 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession(); // We want to use the thread's current repo session
275 parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
278 result.CSID = parentcsid;
279 result.shortIdentifier = parentShortIdentifier;
284 public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemSpecifier, String parentCsid, String method, String op)
287 Specifier itemSpec = Specifier.getSpecifier(itemSpecifier, method, op);
289 if (itemSpec.form == SpecifierForm.CSID) {
290 itemCsid = itemSpec.value;
292 CoreSessionInterface repoSession = null;
293 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
295 if (existingContext != null) {
296 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession(); // We want to use the thread's current repo session
299 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentCsid);
300 itemCsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
307 * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then
308 * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
309 * Resource. They then call this method on that resource.
312 public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item)
313 throws Exception, DocumentNotFoundException {
317 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
318 // Ensure we have the right context.
319 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
321 // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
322 NuxeoRepositoryClientImpl client = (NuxeoRepositoryClientImpl)getRepositoryClient(ctx);
323 String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
325 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
326 ctx = createServiceContext(getItemServiceName());
327 DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
328 DocumentModel docModel = docWrapper.getWrappedObject();
334 public Response createAuthority(
335 @Context ResourceMap resourceMap,
336 @Context UriInfo uriInfo,
339 // 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
340 // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
341 // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
342 // the code that creates new authorities. The authority document model handler will first check for authorities with the same short id before
343 // trying to create a new authority.
345 synchronized(AuthorityResource.class) {
347 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
348 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
349 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
351 String csid = getRepositoryClient(ctx).create(ctx, handler);
352 UriBuilder path = UriBuilder.fromResource(resourceClass);
353 path.path("" + csid);
354 Response response = Response.created(path.build()).build();
356 } catch (Exception e) {
357 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
362 protected boolean supportsReplicating(String tenantId, String serviceName) {
363 boolean result = false;
365 ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
366 result = sb.isSupportsReplicating();
372 * Synchronizes the authority and its items/terms with a Shared Authority Server.
374 * @param specifier either a CSID or one of the urn forms
376 * @return the authority
380 public byte[] synchronize(
381 @Context Request request,
382 @Context UriInfo uriInfo,
383 @PathParam("csid") String identifier) {
384 uriInfo = new UriInfoWrapper(uriInfo);
386 boolean neededSync = false;
387 PoxPayloadOut payloadOut = null;
391 // Prevent multiple SAS synchronizations from occurring simultaneously by synchronizing this method.
393 synchronized(AuthorityResource.class) {
395 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
397 * Make sure this authority service supports synchronization
399 if (supportsReplicating(ctx.getTenantId(), ctx.getServiceName()) == false) {
400 throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
402 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
403 specifier = Specifier.getSpecifier(identifier, "getAuthority", "GET");
404 handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
405 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
406 payloadOut = ctx.getOutput();
407 } catch (Exception e) {
408 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, identifier);
412 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
414 if (neededSync == true) {
415 result = payloadOut.getBytes();
417 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
418 specifier.value).getBytes();
419 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
420 throw new CSWebApplicationException(response);
428 * Builds a cached JAX-RS response.
430 protected Response buildResponse(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, PoxPayloadOut payloadOut) {
431 Response result = null;
433 ResponseBuilder responseBuilder = Response.ok(payloadOut.getBytes());
434 this.setCacheControl(ctx, responseBuilder);
435 result = responseBuilder.build();
441 * Gets the authority.
443 * @param specifier either a CSID or one of the urn forms
445 * @return the authority
451 @Context Request request,
452 @Context ResourceMap resourceMap,
453 @Context UriInfo uriInfo,
454 @PathParam("csid") String specifier) {
455 Response result = null;
456 uriInfo = new UriInfoWrapper(uriInfo);
457 PoxPayloadOut payloadout = null;
461 // If the specifier is a fully qualified authority term refname, then return the term payload in the response
463 if (RefNameUtils.isTermRefname(specifier)) {
464 AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(specifier);
465 String parentIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name);
466 String itemIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.name);
467 result = this.getAuthorityItemResponse(request, uriInfo, resourceMap, parentIdentifier, itemIdentifier);
469 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(request, uriInfo);
470 payloadout = getAuthority(ctx, request, uriInfo, specifier, DONT_INCLUDE_ITEMS);
471 result = buildResponse(ctx, payloadout);
473 } catch (Exception e) {
474 throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
477 if (result == null) {
478 Response response = Response.status(Response.Status.NOT_FOUND).entity(
479 "GET request failed. The requested Authority specifier:" + specifier + ": was not found.").type(
480 "text/plain").build();
481 throw new CSWebApplicationException(response);
487 protected PoxPayloadOut getAuthority(
488 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
492 boolean includeItems) throws Exception {
493 uriInfo = new UriInfoWrapper(uriInfo);
494 PoxPayloadOut payloadout = null;
496 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> docHandler = createDocumentHandler(ctx);
497 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
498 if (spec.form == SpecifierForm.CSID) {
499 if (logger.isDebugEnabled()) {
500 logger.debug("getAuthority with csid=" + spec.value);
502 getRepositoryClient(ctx).get(ctx, spec.value, docHandler);
504 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
505 DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
506 docHandler.setDocumentFilter(myFilter);
507 getRepositoryClient(ctx).get(ctx, docHandler);
510 payloadout = ctx.getOutput();
511 if (includeItems == true) {
512 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
513 payloadout.addPart(PoxPayload.ABSTRACT_COMMON_LIST_ROOT_ELEMENT_LABEL, itemsList);
520 * Finds and populates the authority list.
524 * @return the authority list
527 @Produces("application/xml")
528 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.
529 uriInfo = new UriInfoWrapper(uriInfo);
530 AbstractCommonList result = null;
533 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
534 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
536 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
537 DocumentFilter myFilter = handler.getDocumentFilter();
538 // Need to make the default sort order for authority items
539 // be on the displayName field
540 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
541 if (orderBy == null || orderBy.isEmpty()) {
542 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
543 + AuthorityItemJAXBSchema.DISPLAY_NAME;
544 myFilter.setOrderByClause(qualifiedDisplayNameField);
546 String nameQ = queryParams.getFirst("refName");
548 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
550 //getRepositoryClient(ctx).getFiltered(ctx, handler); # Something here?
551 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
552 result = search(ctx, handler, uriInfo, orderBy, null, advancedSearch, null);
553 result = handler.getCommonPartList();
554 } catch (Exception e) {
555 throw bigReThrow(e, ServiceMessages.GET_FAILED);
562 * Overriding this methods to see if we should update the revision number during the update. We don't
563 * want to update the rev number of synchronization operations.
566 protected PoxPayloadOut update(String csid,
567 PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
568 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
570 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
571 Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
572 if (shouldUpdateRev != null) {
573 handler.setShouldUpdateRevNumber(shouldUpdateRev);
575 getRepositoryClient(ctx).update(ctx, csid, handler);
576 return ctx.getOutput();
582 * @param specifier the csid or id
584 * @return the multipart output
588 public byte[] updateAuthority(
589 @Context Request request,
590 @Context ResourceMap resourceMap,
591 @Context UriInfo uriInfo,
592 @PathParam("csid") String specifier,
594 PoxPayloadOut result = null;
596 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
597 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
598 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
599 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
600 String csid = getCsid(ctx, spec);
601 getRepositoryClient(ctx).update(ctx, csid, handler);
602 result = ctx.getOutput();
603 } catch (Exception e) {
604 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
606 return result.getBytes();
610 * Delete all the items in an authority list.
617 @Path("{csid}/items")
618 public Response deleteAuthorityItemList(@PathParam("csid") String specifier,
619 @Context UriInfo uriInfo) {
620 uriInfo = new UriInfoWrapper(uriInfo);
623 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
624 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
626 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
628 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
630 // Delete all the items one by one
632 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
633 for (ListItem item : itemsList.getListItem()) {
634 deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
636 } catch (Throwable t) {
637 repoSession.setTransactionRollbackOnly();
640 repoClient.releaseRepositorySession(ctx, repoSession);
643 return Response.status(HttpResponseCodes.SC_OK).build();
644 } catch (Exception e) {
645 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
652 * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
654 * @return the response
658 public Response deleteAuthority( // # Delete this authority and all of it's items.
659 @Context Request request,
660 @Context UriInfo uriInfo,
661 @PathParam("csid") String specifier) {
662 uriInfo = new UriInfoWrapper(uriInfo);
664 if (logger.isDebugEnabled()) {
665 logger.debug("deleteAuthority with specifier=" + specifier);
669 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
670 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
671 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
673 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
675 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
677 // First try to delete all the items
679 AbstractCommonList itemsList = this.getAuthorityItemList(ctx, specifier, uriInfo);
680 for (ListItem item : itemsList.getListItem()) {
681 deleteAuthorityItem(ctx, specifier, getCsid(item), AuthorityServiceUtils.UPDATE_REV);
685 // Lastly, delete the parent/container
687 if (spec.form == SpecifierForm.CSID) {
688 if (logger.isDebugEnabled()) {
689 logger.debug("deleteAuthority with csid=" + spec.value);
691 ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
692 getRepositoryClient(ctx).delete(ctx, spec.value, handler);
694 if (logger.isDebugEnabled()) {
695 logger.debug("deleteAuthority with specifier=" + spec.value);
697 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
698 getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
700 } catch (Throwable t) {
701 repoSession.setTransactionRollbackOnly();
704 repoClient.releaseRepositorySession(ctx, repoSession);
707 return Response.status(HttpResponseCodes.SC_OK).build();
708 } catch (Exception e) {
709 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
713 protected String getCsid(ListItem item) {
714 String result = null;
716 for (Element ele : item.getAny()) {
717 String elementName = ele.getTagName().toLowerCase();
718 if (elementName.equals("csid")) {
719 result = ele.getTextContent();
730 * @param parentspecifier - ID of the container. Can be URN or CSID form
731 * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
732 * @param isProposed - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
736 protected Response createAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String parentIdentifier,
737 boolean shouldUpdateRevNumber,
739 boolean isSasItem) throws Exception {
740 Response result = null;
742 // Note: must have the parentShortId, to do the create.
743 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
744 AuthorityItemDocumentModelHandler handler =
745 (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
746 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
747 handler.setIsProposed(isProposed);
748 handler.setIsSASItem(isSasItem);
749 // Make the client call
750 String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
752 // Build the JAX-RS response
753 UriBuilder path = UriBuilder.fromResource(resourceClass);
754 path.path(parent.CSID + "/items/" + itemcsid);
755 result = Response.created(path.build()).build();
760 public PoxPayloadOut updateAuthorityItem(
761 ServiceContext<PoxPayloadIn, PoxPayloadOut> itemServiceCtx, // Ok to be null. Will be null on PUT calls, but not on sync calls
762 ResourceMap resourceMap,
764 String parentspecifier,
765 String itemspecifier,
766 PoxPayloadIn theUpdate,
767 boolean shouldUpdateRevNumber,
771 PoxPayloadOut result = null;
773 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
774 String parentcsid = csidAndShortId.CSID;
775 String parentShortId = csidAndShortId.shortIdentifier;
777 // If the itemServiceCtx context is not null, use it. Otherwise, create a new context
779 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
781 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
783 ctx.setInput(theUpdate); // the update payload
786 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
788 // We omit the parentShortId, only needed when doing a create...
789 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
790 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
792 // Update the SAS fields if either value is non-null
794 boolean updateSASFields = isProposed != null || isSASItem != null;
795 handler.setshouldUpdateSASFields(updateSASFields);
796 if (updateSASFields == true) {
797 handler.setshouldUpdateSASFields(true);
798 if (isProposed != null) {
799 handler.setIsProposed(isProposed);
801 if (isSASItem != null) {
802 handler.setIsSASItem(isSASItem);
806 getRepositoryClient(ctx).update(ctx, itemcsid, handler);
807 result = ctx.getOutput();
813 * Called with an existing context.
815 * @param parentIdentifier
820 public Response createAuthorityItemWithParentContext(ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx,
821 String parentIdentifier,
823 boolean shouldUpdateRevNumber,
825 boolean isSASItem) throws Exception {
826 Response result = null;
828 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
829 parentCtx.getResourceMap(), parentCtx.getUriInfo());
830 if (parentCtx.getCurrentRepositorySession() != null) {
831 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
833 result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
838 /*************************************************************************
839 * Create an AuthorityItem - this is a sub-resource of Authority
840 * @param specifier either a CSID or one of the urn forms
841 * @return Authority item response
842 *************************************************************************/
844 @Path("{csid}/items")
845 public Response createAuthorityItem(
846 @Context ResourceMap resourceMap,
847 @Context UriInfo uriInfo,
848 @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
850 uriInfo = new UriInfoWrapper(uriInfo);
851 Response result = null;
854 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
855 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
856 result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
857 AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
858 } catch (Exception e) {
859 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
866 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
867 public byte[] getItemWorkflow(
868 @PathParam("csid") String csid,
869 @PathParam("itemcsid") String itemcsid) {
870 PoxPayloadOut result = null;
873 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
874 String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
876 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
877 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
878 ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
879 getRepositoryClient(ctx).get(ctx, itemcsid, handler);
880 result = ctx.getOutput();
881 } catch (Exception e) {
882 throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
884 return result.getBytes();
888 * We should consider changing this code. The RepositoryClient (from call to getRepositoryClient) could support a call doWorkflowTransition() instead?
892 @Path("{csid}" + WorkflowClient.SERVICE_PATH + "/" + "{transition}")
893 public byte[] updateWorkflowWithTransition(
894 @Context UriInfo uriInfo,
895 @PathParam("csid") String specifier,
896 @PathParam("transition") String transition) {
897 PoxPayloadOut result = null;
899 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
902 csid = getCsid(null, spec);
903 result = updateWorkflowWithTransition(NULL_CONTEXT, uriInfo, csid, transition);
904 } catch (Exception e) {
905 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
908 return result.getBytes();
911 //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
912 // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
914 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
915 public byte[] updateItemWorkflowWithTransition(
916 @Context UriInfo uriInfo,
917 @PathParam("csid") String parentIdentifier,
918 @PathParam("itemcsid") String itemIdentifier,
919 @PathParam("transition") String transition) {
920 uriInfo = new UriInfoWrapper(uriInfo);
921 PoxPayloadOut result = null;
924 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
925 result = updateItemWorkflowWithTransition(ctx,
926 parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
927 } catch (Exception e) {
928 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
931 return result.getBytes();
934 public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
935 String parentIdentifier,
936 String itemIdentifier,
938 boolean updateRevNumber) throws DocumentReferenceException {
939 return updateItemWorkflowWithTransition(existingContext, parentIdentifier, itemIdentifier, transition, updateRevNumber, true);
943 * Update an authority item's workflow state.
944 * @param existingContext
949 * @throws DocumentReferenceException
951 public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
952 String parentIdentifier,
953 String itemIdentifier,
955 boolean updateRevNumber,
956 boolean rollbackOnException) throws DocumentReferenceException {
957 PoxPayloadOut result = null;
961 // We need CSIDs for both the parent authority and the authority item
963 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
964 String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
967 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
969 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(),
970 WorkflowClient.SERVICE_COMMONPART_NAME);
971 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
972 ctx.setRollbackOnException(rollbackOnException);
973 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
974 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
977 // Create a service context and document handler for the target resource -not the workflow resource itself.
979 ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
980 AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
981 targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
982 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
984 // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
986 String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
987 ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
989 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
990 TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
991 if (transitionDef == null) {
992 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
993 itemIdentifier, transition));
995 ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
997 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
998 getRepositoryClient(ctx).update(ctx, itemCsid, handler);
999 result = ctx.getOutput();
1000 } catch (DocumentReferenceException de) {
1002 } catch (Exception e) {
1003 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
1009 protected PoxPayloadOut getAuthorityItem(
1010 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1011 String parentIdentifier,
1012 String itemIdentifier) throws Exception {
1013 PoxPayloadOut result = null;
1015 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
1016 // We omit the parentShortId, only needed when doing a create...
1017 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
1019 Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
1020 if (itemSpec.form == SpecifierForm.CSID) {
1021 // TODO should we assert that the item is in the passed vocab?
1022 getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
1024 String itemWhereClause =
1025 RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
1026 DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
1027 handler.setDocumentFilter(myFilter);
1028 getRepositoryClient(ctx).get(ctx, handler);
1031 result = (PoxPayloadOut) ctx.getOutput();
1032 if (result != null) {
1033 String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
1034 if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
1035 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
1036 itemSpec.value, inAuthority, parentcsid));
1043 public PoxPayloadOut getAuthorityItemWithExistingContext(
1044 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1045 String parentIdentifier,
1046 String itemIdentifier) throws Exception {
1047 PoxPayloadOut result = null;
1049 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
1050 if (existingCtx.getCurrentRepositorySession() != null) {
1051 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
1052 ctx.setProperties(existingCtx.getProperties());
1054 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1060 * Gets the authority item.
1062 * @param parentspecifier either a CSID or one of the urn forms
1063 * @param itemspecifier either a CSID or one of the urn forms
1065 * @return the authority item
1068 @Path("{csid}/items/{itemcsid}")
1069 public byte[] getAuthorityItem(
1070 @Context Request request,
1071 @Context UriInfo uriInfo,
1072 @Context ResourceMap resourceMap,
1073 @PathParam("csid") String parentIdentifier,
1074 @PathParam("itemcsid") String itemIdentifier) {
1075 uriInfo = new UriInfoWrapper(uriInfo);
1076 PoxPayloadOut result = null;
1078 result = this.getAuthorityItemPayload(request, uriInfo, resourceMap, parentIdentifier, itemIdentifier);
1080 return result.getBytes();
1084 public PoxPayloadOut getAuthorityItemPayload(
1085 @Context Request request,
1086 @Context UriInfo uriInfo,
1087 @Context ResourceMap resourceMap,
1088 @PathParam("csid") String parentIdentifier,
1089 @PathParam("itemcsid") String itemIdentifier) {
1090 uriInfo = new UriInfoWrapper(uriInfo);
1091 PoxPayloadOut result = null;
1093 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx =
1094 (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1096 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1097 ctx.setJaxRsContext(jaxRsContext);
1099 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1100 } catch (DocumentNotFoundException dnf) {
1101 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1102 } catch (Exception e) {
1103 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1109 public Response getAuthorityItemResponse(
1110 @Context Request request,
1111 @Context UriInfo uriInfo,
1112 @Context ResourceMap resourceMap,
1113 @PathParam("csid") String parentIdentifier,
1114 @PathParam("itemcsid") String itemIdentifier) {
1115 uriInfo = new UriInfoWrapper(uriInfo);
1116 PoxPayloadOut payloadout = null;
1117 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = null;
1120 ctx = (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
1122 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
1123 ctx.setJaxRsContext(jaxRsContext);
1125 payloadout = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
1126 } catch (DocumentNotFoundException dnf) {
1127 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
1128 } catch (Exception e) {
1129 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1132 return buildResponse(ctx, payloadout);
1137 * Most of the authority child classes will/should use this implementation. However, the Vocabulary service's item schema is
1138 * different enough that it will have to override this method in it's resource class.
1141 protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1142 String result = null;
1144 result = NuxeoUtils.getPrimaryElPathPropertyName(
1145 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1146 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1152 protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1153 String result = null;
1155 result = NuxeoUtils.getMultiElPathPropertyName(
1156 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
1157 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
1163 * Gets the authorityItem list for the specified authority
1164 * If partialPerm is specified, keywords will be ignored.
1166 * @param authorityIdentifier either a CSID or one of the urn forms
1167 * @param partialTerm if non-null, matches partial terms
1168 * @param keywords if non-null, matches terms in the keyword index for items
1169 * @param ui passed to include additional parameters, like pagination controls
1172 public AbstractCommonList getAuthorityItemList(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1173 String authorityIdentifier,
1174 UriInfo uriInfo) throws Exception {
1175 AbstractCommonList result = null;
1177 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1178 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1179 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
1180 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
1181 ctx.setProperties(existingContext.getProperties());
1184 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
1185 String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
1186 String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
1187 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
1188 String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
1190 // For the wildcard case, parentcsid is null, but docHandler will deal with this.
1191 // We omit the parentShortId, only needed when doing a create...
1192 String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
1193 lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
1194 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
1195 createItemDocumentHandler(ctx, parentcsid, null);
1197 DocumentFilter myFilter = handler.getDocumentFilter();
1198 // If we are not wildcarding the parent, add a restriction
1199 if (parentcsid != null) {
1200 myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
1201 + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
1202 + "'" + parentcsid + "'",
1203 IQueryManager.SEARCH_QUALIFIER_AND);
1206 if (Tools.notBlank(termStatus)) {
1207 // Start with the qualified termStatus field
1208 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
1209 + AuthorityItemJAXBSchema.TERM_STATUS;
1210 String[] filterTerms = termStatus.trim().split("\\|");
1211 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
1212 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
1215 result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
1221 * Gets the authorityItem list for the specified authority
1222 * If partialPerm is specified, keywords will be ignored.
1224 * @param authorityIdentifier either a CSID or one of the urn forms
1225 * @param partialTerm if non-null, matches partial terms
1226 * @param keywords if non-null, matches terms in the keyword index for items
1227 * @param ui passed to include additional parameters, like pagination controls
1229 * @return the authorityItem list
1232 @Path("{csid}/items")
1233 @Produces("application/xml")
1234 public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
1235 @Context UriInfo uriInfo) {
1236 uriInfo = new UriInfoWrapper(uriInfo);
1237 AbstractCommonList result = null;
1240 result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);
1241 } catch (Exception e) {
1242 throw bigReThrow(e, ServiceMessages.LIST_FAILED);
1249 * @return the name of the property used to specify references for items in this type of
1250 * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
1251 * Some types (like Vocabulary) use a separate property.
1253 protected String getRefPropName() {
1254 return ServiceBindingUtils.AUTH_REF_PROP;
1258 * Gets the entities referencing this Authority item instance. The service type
1259 * can be passed as a query param "type", and must match a configured type
1260 * for the service bindings. If not set, the type defaults to
1261 * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1263 * @param parentspecifier either a CSID or one of the urn forms
1264 * @param itemspecifier either a CSID or one of the urn forms
1267 * @return the info for the referencing objects
1270 @Path("{csid}/items/{itemcsid}/refObjs")
1271 @Produces("application/xml")
1272 public AuthorityRefDocList getReferencingObjects(
1273 @PathParam("csid") String parentSpecifier,
1274 @PathParam("itemcsid") String itemSpecifier,
1275 @Context UriInfo uriInfo) {
1276 uriInfo = new UriInfoWrapper(uriInfo);
1277 AuthorityRefDocList authRefDocList = null;
1279 authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriInfo, PAGE_NUM_FROM_QUERYPARAMS, PAGE_SIZE_FROM_QUERYPARAMS, true, true);
1280 } catch (Exception e) {
1281 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1284 if (authRefDocList == null) {
1285 Response response = Response.status(Response.Status.NOT_FOUND).entity(
1286 "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1287 "text/plain").build();
1288 throw new CSWebApplicationException(response);
1290 return authRefDocList;
1293 public AuthorityRefDocList getReferencingObjects(
1294 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
1295 String parentspecifier,
1296 String itemspecifier,
1300 boolean useDefaultOrderByClause,
1301 boolean computeTotal) throws Exception {
1302 AuthorityRefDocList authRefDocList = null;
1304 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1305 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1307 // Merge parts of existing context with our new context
1309 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1310 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If one exists, use the existing repo session
1311 ctx.setProperties(existingContext.getProperties());
1314 String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1315 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1317 // Remove the "type" property from the query params
1318 List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1319 if (serviceTypes == null || serviceTypes.isEmpty()) {
1320 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1323 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1324 authRefDocList = handler.getReferencingObjects(ctx, serviceTypes, getRefPropName(), itemcsid, pageNum, pageSize, useDefaultOrderByClause, computeTotal);
1326 return authRefDocList;
1330 * Gets the authority terms used in the indicated Authority item.
1332 * @param parentspecifier either a CSID or one of the urn forms
1333 * @param itemspecifier either a CSID or one of the urn forms
1334 * @param ui passed to include additional parameters, like pagination controls
1336 * @return the authority refs for the Authority item.
1339 @Path("{csid}/items/{itemcsid}/authorityrefs")
1340 @Produces("application/xml")
1341 public AuthorityRefList getAuthorityItemAuthorityRefs(
1342 @PathParam("csid") String parentspecifier,
1343 @PathParam("itemcsid") String itemspecifier,
1344 @Context UriInfo uriInfo) {
1345 uriInfo = new UriInfoWrapper(uriInfo);
1346 AuthorityRefList authRefList = null;
1349 // Note that we have to create the service context for the Items, not the main service
1350 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1351 String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1352 // We omit the parentShortId, only needed when doing a create...
1353 DocumentModelHandler<?, AbstractCommonList> handler =
1354 (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1356 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1358 List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1359 authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1360 } catch (Exception e) {
1361 throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1368 * Synchronizes a local authority item with a share authority server (SAS) item.
1370 * @param parentIdentifier
1371 * @param itemIdentifier
1375 private PoxPayloadOut synchronizeItem(
1376 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1377 String parentIdentifier,
1378 String itemIdentifier,
1379 boolean syncHierarchicalRelationships) throws Exception {
1380 PoxPayloadOut result = null;
1381 AuthorityItemSpecifier specifier;
1382 boolean neededSync = false;
1384 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1385 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1386 handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1387 handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1388 handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1389 // Create an authority item specifier
1390 Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1391 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1392 specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1394 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1395 if (neededSync == true) {
1396 result = (PoxPayloadOut) ctx.getOutput();
1403 * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1404 * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1405 * local authority. The parent context was created for the authority (parent) because the sync started there.
1406 * @param existingCtx
1407 * @param parentIdentifier
1408 * @param itemIdentifier
1412 public PoxPayloadOut synchronizeItemWithExistingContext(
1413 ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1414 String parentIdentifier,
1415 String itemIdentifier,
1416 boolean syncHierarchicalRelationships
1417 ) throws Exception {
1418 PoxPayloadOut result = null;
1420 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1421 existingCtx.getResourceMap(),
1422 existingCtx.getUriInfo());
1423 if (existingCtx.getCurrentRepositorySession() != null) {
1424 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1427 result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1433 * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1435 * @param specifier either CSIDs and/or one of the urn forms
1437 * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1440 @Path("{csid}/items/{itemcsid}/sync")
1441 public byte[] synchronizeItem(
1442 @Context ResourceMap resourceMap,
1443 @Context UriInfo uriInfo,
1444 @PathParam("csid") String parentIdentifier,
1445 @PathParam("itemcsid") String itemIdentifier) {
1446 uriInfo = new UriInfoWrapper(uriInfo);
1448 boolean neededSync = false;
1449 PoxPayloadOut payloadOut = null;
1452 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1453 payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1454 if (payloadOut != null) {
1457 } catch (Exception e) {
1458 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1462 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
1464 if (neededSync == true) {
1465 result = payloadOut.getBytes();
1467 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1468 itemIdentifier).getBytes();
1469 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1470 throw new CSWebApplicationException(response);
1477 * Update authorityItem.
1479 * @param parentspecifier either a CSID or one of the urn forms
1480 * @param itemspecifier either a CSID or one of the urn forms
1482 * @return the multipart output
1485 @Path("{csid}/items/{itemcsid}")
1486 public byte[] updateAuthorityItem(
1487 @Context ResourceMap resourceMap,
1488 @Context UriInfo uriInfo,
1489 @PathParam("csid") String parentSpecifier,
1490 @PathParam("itemcsid") String itemSpecifier,
1491 String xmlPayload) {
1492 uriInfo = new UriInfoWrapper(uriInfo);
1493 PoxPayloadOut result = null;
1496 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1497 result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1498 AuthorityServiceUtils.UPDATE_REV, // passing TRUE so rev num increases, passing
1499 AuthorityServiceUtils.NO_CHANGE, // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1500 AuthorityServiceUtils.NO_CHANGE); // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1501 } catch (Exception e) {
1502 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1505 return result.getBytes();
1511 * Delete authorityItem.
1513 * @param parentIdentifier the parentcsid
1514 * @param itemIdentifier the itemcsid
1516 * @return the response
1519 @Path("{csid}/items/{itemcsid}")
1520 public Response deleteAuthorityItem(
1521 @Context UriInfo uriInfo,
1522 @PathParam("csid") String parentIdentifier,
1523 @PathParam("itemcsid") String itemIdentifier) {
1524 uriInfo = new UriInfoWrapper(uriInfo);
1525 Response result = null;
1527 ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1528 ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1529 if (logger.isDebugEnabled()) {
1530 logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1534 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1535 deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1536 result = Response.status(HttpResponseCodes.SC_OK).build();
1537 } catch (Exception e) {
1538 throw bigReThrow(e, ServiceMessages.DELETE_FAILED + " itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1544 public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1545 String parentIdentifier,
1546 String itemIdentifier,
1547 boolean shouldUpdateRevNumber) throws Exception {
1548 return deleteAuthorityItem(existingCtx, parentIdentifier, itemIdentifier, shouldUpdateRevNumber, true);
1553 * @param existingCtx
1554 * @param parentIdentifier
1555 * @param itemIdentifier
1558 public boolean deleteAuthorityItem(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingCtx,
1559 String parentIdentifier,
1560 String itemIdentifier,
1561 boolean shouldUpdateRevNumber,
1562 boolean rollbackOnException
1563 ) throws Exception {
1564 boolean result = true;
1566 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1567 ctx.setRollbackOnException(rollbackOnException);
1568 if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1569 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1570 ctx.setProperties(existingCtx.getProperties());
1573 String parentcsid = null;
1575 parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1576 } catch (DocumentNotFoundException de) {
1577 String msg = String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1578 parentIdentifier, itemIdentifier);
1582 String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1584 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1585 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1586 result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1592 @Path("{csid}/items/{itemcsid}/" + hierarchy)
1593 @Produces("application/xml")
1594 public String getHierarchy(
1595 @PathParam("csid") String parentIdentifier,
1596 @PathParam("itemcsid") String itemIdentifier,
1597 @Context UriInfo uriInfo) throws Exception {
1598 uriInfo = new UriInfoWrapper(uriInfo);
1599 String result = null;
1603 // 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...?
1605 String calledUri = uriInfo.getPath();
1606 String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1607 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1609 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1610 String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1612 String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1613 if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1614 result = Hierarchy.surface(ctx, itemcsid, uri);
1616 result = Hierarchy.dive(ctx, itemcsid, uri);
1618 } catch (Exception e) {
1619 throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1630 public String getItemDocType(String tenantId) {
1631 return getDocType(tenantId, getItemServiceName());
1635 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1636 * for the current resource, for all tenants
1638 * @return a map of URI templates for the current resource, for all tenants
1641 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1642 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1643 super.getUriRegistryEntries();
1644 List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1645 for (String tenantId : tenantIds) {
1646 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1648 return uriRegistryEntriesMap;
1655 public ServiceDescription getDescription(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
1656 ServiceDescription result = super.getDescription(ctx);
1657 result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));
1661 public Response createAuthority(String xmlPayload) {
1662 return this.createAuthority(null, null, xmlPayload);
1665 protected String getCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, Specifier specifier) throws Exception {
1669 ctx = createServiceContext(getServiceName());
1672 if (specifier.form == SpecifierForm.CSID) {
1673 csid = specifier.value;
1675 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, specifier.value);
1676 csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);