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.PoxPayloadIn;
48 import org.collectionspace.services.client.PoxPayloadOut;
49 import org.collectionspace.services.client.XmlTools;
50 import org.collectionspace.services.client.workflow.WorkflowClient;
51 import org.collectionspace.services.common.CSWebApplicationException;
52 import org.collectionspace.services.common.NuxeoBasedResource;
53 import org.collectionspace.services.common.ResourceMap;
54 import org.collectionspace.services.common.ServiceMain;
55 import org.collectionspace.services.common.ServiceMessages;
56 import org.collectionspace.services.common.StoredValuesUriTemplate;
57 import org.collectionspace.services.common.UriInfoWrapper;
58 import org.collectionspace.services.common.UriTemplateFactory;
59 import org.collectionspace.services.common.UriTemplateRegistry;
60 import org.collectionspace.services.common.UriTemplateRegistryKey;
61 import org.collectionspace.services.common.api.RefName;
62 import org.collectionspace.services.common.api.Tools;
63 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
64 import org.collectionspace.services.common.authorityref.AuthorityRefList;
65 import org.collectionspace.services.common.context.JaxRsContext;
66 import org.collectionspace.services.common.context.MultipartServiceContext;
67 import org.collectionspace.services.common.context.RemoteServiceContext;
68 import org.collectionspace.services.common.context.ServiceBindingUtils;
69 import org.collectionspace.services.common.context.ServiceContext;
70 import org.collectionspace.services.common.document.DocumentException;
71 import org.collectionspace.services.common.document.DocumentFilter;
72 import org.collectionspace.services.common.document.DocumentHandler;
73 import org.collectionspace.services.common.document.DocumentNotFoundException;
74 import org.collectionspace.services.common.document.DocumentReferenceException;
75 import org.collectionspace.services.common.document.DocumentWrapper;
76 import org.collectionspace.services.common.document.Hierarchy;
77 import org.collectionspace.services.common.query.QueryManager;
78 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
79 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
80 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
81 import org.collectionspace.services.config.ClientType;
82 import org.collectionspace.services.config.service.ServiceBindingType;
83 import org.collectionspace.services.jaxb.AbstractCommonList;
84 import org.collectionspace.services.lifecycle.TransitionDef;
85 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
86 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
87 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentFilter;
88 import org.collectionspace.services.nuxeo.client.java.RepositoryClientImpl;
89 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
90 import org.collectionspace.services.workflow.WorkflowCommon;
91 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
92 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
93 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
94 import org.collectionspace.services.description.ServiceDescription;
95 import org.jboss.resteasy.util.HttpResponseCodes;
96 import org.nuxeo.ecm.core.api.DocumentModel;
97 import org.nuxeo.ecm.core.api.DocumentModelList;
98 import org.slf4j.Logger;
99 import org.slf4j.LoggerFactory;
102 * The Class AuthorityResource.
105 @Consumes("application/xml")
106 @Produces("application/xml")
107 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
108 extends NuxeoBasedResource {
110 final static String SEARCH_TYPE_TERMSTATUS = "ts";
111 public final static String hierarchy = "hierarchy";
113 protected Class<AuthCommon> authCommonClass;
114 protected Class<?> resourceClass;
115 protected String authorityCommonSchemaName;
116 protected String authorityItemCommonSchemaName;
117 final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed? I see no references to it.
119 final static String FETCH_SHORT_ID = "_fetch_";
120 public final static String PARENT_WILDCARD = "_ALL_";
122 final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
125 * Instantiates a new Authority resource.
127 public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
128 String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
129 this.authCommonClass = authCommonClass;
130 this.resourceClass = resourceClass;
131 this.authorityCommonSchemaName = authorityCommonSchemaName;
132 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
135 public abstract String getItemServiceName();
137 public abstract String getItemTermInfoGroupXPathBase();
140 protected String getVersionString() {
141 return "$LastChangedRevision: 2617 $";
145 public Class<AuthCommon> getCommonPartClass() {
146 return authCommonClass;
150 * Creates the item document handler.
153 * @param inAuthority the in vocabulary
155 * @return the document handler
157 * @throws Exception the exception
159 protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
160 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
161 String inAuthority, String parentShortIdentifier)
163 String authorityRefNameBase;
164 AuthorityItemDocumentModelHandler<?> docHandler;
166 if (parentShortIdentifier == null) {
167 authorityRefNameBase = null;
169 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getServiceName());
170 if (parentShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
171 if (ctx.getCurrentRepositorySession() != null) {
172 parentCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
174 // Get from parent document
175 parentShortIdentifier = getAuthShortIdentifier(parentCtx, inAuthority);
177 authorityRefNameBase = buildAuthorityRefNameBase(parentCtx, parentShortIdentifier);
180 docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
181 ctx.getCommonPartLabel(getItemServiceName()),
183 // FIXME - Richard and Aron think the following three lines should
184 // be in the constructor for the AuthorityItemDocumentModelHandler
185 // because all three are required fields.
186 docHandler.setInAuthority(inAuthority);
187 docHandler.setAuthorityRefNameBase(authorityRefNameBase);
188 docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
192 public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
193 throws DocumentNotFoundException, DocumentException {
194 String shortIdentifier = null;
197 AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
198 shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
199 } catch (Exception e) {
200 if (logger.isDebugEnabled()) {
201 logger.debug("Caught exception ", e);
203 throw new DocumentException(e);
206 return shortIdentifier;
209 protected String buildAuthorityRefNameBase(
210 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
211 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
212 ctx.getServiceName(),
213 null, // Only use shortId form!!!
214 shortIdentifier, null);
215 return authority.toString();
218 public static class CsidAndShortIdentifier {
220 String shortIdentifier;
223 protected String lookupParentCSID(String parentspecifier, String method,
224 String op, UriInfo uriInfo) throws Exception {
225 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(NULL_CONTEXT,
226 parentspecifier, method, op, uriInfo);
227 return tempResult.CSID;
230 protected String lookupParentCSID(ServiceContext ctx, String parentspecifier, String method,
231 String op, UriInfo uriInfo) throws Exception {
232 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
233 parentspecifier, method, op, uriInfo);
234 return tempResult.CSID;
238 private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
239 ServiceContext existingCtx, // Ok to be null
240 String parentIdentifier,
245 CsidAndShortIdentifier result = new CsidAndShortIdentifier();
246 Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
249 String parentShortIdentifier;
250 if (parentSpec.form == SpecifierForm.CSID) {
251 parentShortIdentifier = null;
252 parentcsid = parentSpec.value;
253 // Uncomment when app layer is ready to integrate
254 // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
255 parentShortIdentifier = FETCH_SHORT_ID;
257 parentShortIdentifier = parentSpec.value;
258 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
259 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
260 CoreSessionInterface repoSession = null;
261 if (existingCtx != null) {
262 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession(); // We want to use the thread's current repo session
264 parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
267 result.CSID = parentcsid;
268 result.shortIdentifier = parentShortIdentifier;
273 public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemspecifier, String parentcsid, String method, String op)
277 Specifier itemSpec = Specifier.getSpecifier(itemspecifier, method, op);
278 if (itemSpec.form == SpecifierForm.CSID) {
279 itemcsid = itemSpec.value;
281 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
282 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
283 CoreSessionInterface repoSession = null;
284 if (existingContext != null) {
285 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession(); // We want to use the thread's current repo session
287 itemcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
294 * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then
295 * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
296 * Resource. They then call this method on that resource.
299 public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item)
300 throws Exception, DocumentNotFoundException {
304 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
305 // Ensure we have the right context.
306 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
308 // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
309 RepositoryClientImpl client = (RepositoryClientImpl)getRepositoryClient(ctx);
310 String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
312 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
313 ctx = createServiceContext(getItemServiceName());
314 DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
315 DocumentModel docModel = docWrapper.getWrappedObject();
321 public Response createAuthority(String xmlPayload) {
323 // 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
324 // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
325 // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
326 // the code that creates new authorities. The authority document model handler will first check for authorities with the same short id before
327 // trying to create a new authority.
329 synchronized(AuthorityResource.class) {
331 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
332 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
333 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
335 String csid = getRepositoryClient(ctx).create(ctx, handler);
336 UriBuilder path = UriBuilder.fromResource(resourceClass);
337 path.path("" + csid);
338 Response response = Response.created(path.build()).build();
340 } catch (Exception e) {
341 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
346 protected boolean supportsReplicating(String tenantId, String serviceName) {
347 boolean result = false;
349 ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
350 result = sb.isSupportsReplicating();
356 * Synchronizes the authority and its items/terms with a Shared Authority Server.
358 * @param specifier either a CSID or one of the urn forms
360 * @return the authority
364 public byte[] synchronize(
365 @Context Request request,
366 @Context UriInfo uriInfo,
367 @PathParam("csid") String identifier) {
368 uriInfo = new UriInfoWrapper(uriInfo);
370 boolean neededSync = false;
371 PoxPayloadOut payloadOut = null;
375 // Prevent multiple SAS synchronizations from occurring simultaneously by synchronizing this method.
377 synchronized(AuthorityResource.class) {
379 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
381 * Make sure this authority service supports synchronization
383 if (supportsReplicating(ctx.getTenantId(), ctx.getServiceName()) == false) {
384 throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
386 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
387 specifier = Specifier.getSpecifier(identifier, "getAuthority", "GET");
388 handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
389 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
390 payloadOut = ctx.getOutput();
391 } catch (Exception e) {
392 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, identifier);
396 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
398 if (neededSync == true) {
399 result = payloadOut.getBytes();
401 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
402 specifier.value).getBytes();
403 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
404 throw new CSWebApplicationException(response);
412 * Gets the authority.
414 * @param specifier either a CSID or one of the urn forms
416 * @return the authority
422 @Context Request request,
423 @Context UriInfo uriInfo,
424 @PathParam("csid") String specifier) {
425 Response result = null;
426 uriInfo = new UriInfoWrapper(uriInfo);
427 PoxPayloadOut payloadout = null;
430 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(request, uriInfo);
431 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
433 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
434 if (spec.form == SpecifierForm.CSID) {
435 if (logger.isDebugEnabled()) {
436 logger.debug("getAuthority with csid=" + spec.value);
438 getRepositoryClient(ctx).get(ctx, spec.value, handler);
440 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
441 DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
442 handler.setDocumentFilter(myFilter);
443 getRepositoryClient(ctx).get(ctx, handler);
446 payloadout = ctx.getOutput();
447 ResponseBuilder responseBuilder = Response.ok(payloadout.getBytes());
448 this.setCacheControl(ctx, responseBuilder);
449 result = responseBuilder.build();
450 } catch (Exception e) {
451 throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
454 if (result == null) {
455 Response response = Response.status(Response.Status.NOT_FOUND).entity(
456 "GET request failed. The requested Authority specifier:" + specifier + ": was not found.").type(
457 "text/plain").build();
458 throw new CSWebApplicationException(response);
465 * Finds and populates the authority list.
469 * @return the authority list
472 @Produces("application/xml")
473 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.
474 uriInfo = new UriInfoWrapper(uriInfo);
475 AbstractCommonList result = null;
478 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
479 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
481 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
482 DocumentFilter myFilter = handler.getDocumentFilter();
483 // Need to make the default sort order for authority items
484 // be on the displayName field
485 String sortBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
486 if (sortBy == null || sortBy.isEmpty()) {
487 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
488 + AuthorityItemJAXBSchema.DISPLAY_NAME;
489 myFilter.setOrderByClause(qualifiedDisplayNameField);
491 String nameQ = queryParams.getFirst("refName");
493 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
495 getRepositoryClient(ctx).getFiltered(ctx, handler);
496 result = handler.getCommonPartList();
497 } catch (Exception e) {
498 throw bigReThrow(e, ServiceMessages.GET_FAILED);
505 * Overriding this methods to see if we should update the revision number during the update. We don't
506 * want to update the rev number of synchronization operations.
509 protected PoxPayloadOut update(String csid,
510 PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
511 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
513 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
514 Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
515 if (shouldUpdateRev != null) {
516 handler.setShouldUpdateRevNumber(shouldUpdateRev);
518 getRepositoryClient(ctx).update(ctx, csid, handler);
519 return ctx.getOutput();
525 * @param specifier the csid or id
527 * @return the multipart output
531 public byte[] updateAuthority(
532 @PathParam("csid") String specifier,
534 PoxPayloadOut result = null;
536 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
537 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
538 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
539 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
541 if (spec.form == SpecifierForm.CSID) {
544 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
545 csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
547 getRepositoryClient(ctx).update(ctx, csid, handler);
548 result = ctx.getOutput();
549 } catch (Exception e) {
550 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
552 return result.getBytes();
558 * @param csid the csid
560 * @return the response
565 public Response old_deleteAuthority(@PathParam("csid") String csid) {
566 if (logger.isDebugEnabled()) {
567 logger.debug("deleteAuthority with csid=" + csid);
570 ensureCSID(csid, ServiceMessages.DELETE_FAILED, "Authority.csid");
571 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
572 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
573 getRepositoryClient(ctx).delete(ctx, csid, handler);
574 return Response.status(HttpResponseCodes.SC_OK).build();
575 } catch (Exception e) {
576 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, csid);
583 * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
585 * @return the response
589 public Response deleteAuthority(
590 @Context Request request,
591 @Context UriInfo uriInfo,
592 @PathParam("csid") String specifier) {
593 uriInfo = new UriInfoWrapper(uriInfo);
595 if (logger.isDebugEnabled()) {
596 logger.debug("deleteAuthority with specifier=" + specifier);
600 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
601 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
603 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
604 if (spec.form == SpecifierForm.CSID) {
605 if (logger.isDebugEnabled()) {
606 logger.debug("deleteAuthority with csid=" + spec.value);
608 ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
609 getRepositoryClient(ctx).delete(ctx, spec.value, handler);
611 if (logger.isDebugEnabled()) {
612 logger.debug("deleteAuthority with specifier=" + spec.value);
614 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
615 getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
618 return Response.status(HttpResponseCodes.SC_OK).build();
619 } catch (Exception e) {
620 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
627 * @param parentspecifier - ID of the container. Can be URN or CSID form
628 * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
629 * @param isProposed - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
633 protected Response createAuthorityItem(ServiceContext ctx, String parentIdentifier,
634 boolean shouldUpdateRevNumber,
636 boolean isSasItem) throws Exception {
637 Response result = null;
639 // Note: must have the parentShortId, to do the create.
640 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
641 AuthorityItemDocumentModelHandler handler =
642 (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
643 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
644 handler.setIsProposed(isProposed);
645 handler.setIsSASItem(isSasItem);
646 // Make the client call
647 String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
649 // Build the JAX-RS response
650 UriBuilder path = UriBuilder.fromResource(resourceClass);
651 path.path(parent.CSID + "/items/" + itemcsid);
652 result = Response.created(path.build()).build();
658 * Called with an existing context.
660 * @param parentIdentifier
665 public Response createAuthorityItemWithParentContext(ServiceContext parentCtx,
666 String parentIdentifier,
668 boolean shouldUpdateRevNumber,
670 boolean isSASItem) throws Exception {
671 Response result = null;
673 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
674 parentCtx.getResourceMap(), parentCtx.getUriInfo());
675 if (parentCtx.getCurrentRepositorySession() != null) {
676 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
678 result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
683 /*************************************************************************
684 * Create an AuthorityItem - this is a sub-resource of Authority
685 * @param specifier either a CSID or one of the urn forms
686 * @return Authority item response
687 *************************************************************************/
689 @Path("{csid}/items")
690 public Response createAuthorityItem(
691 @Context ResourceMap resourceMap,
692 @Context UriInfo uriInfo,
693 @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
695 uriInfo = new UriInfoWrapper(uriInfo);
696 Response result = null;
699 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
700 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
701 result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
702 AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
703 } catch (Exception e) {
704 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
711 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
712 public byte[] getItemWorkflow(
713 @PathParam("csid") String csid,
714 @PathParam("itemcsid") String itemcsid) {
715 PoxPayloadOut result = null;
718 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
719 String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
721 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
722 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
723 ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
724 getRepositoryClient(ctx).get(ctx, itemcsid, handler);
725 result = ctx.getOutput();
726 } catch (Exception e) {
727 throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
729 return result.getBytes();
732 //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
733 // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
735 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
736 public byte[] updateItemWorkflowWithTransition(
737 @Context UriInfo uriInfo,
738 @PathParam("csid") String parentIdentifier,
739 @PathParam("itemcsid") String itemIdentifier,
740 @PathParam("transition") String transition) {
741 uriInfo = new UriInfoWrapper(uriInfo);
742 PoxPayloadOut result = null;
745 ServiceContext ctx = createServiceContext(getItemServiceName(), uriInfo);
746 result = updateItemWorkflowWithTransition(ctx,
747 parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
748 } catch (Exception e) {
749 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
752 return result.getBytes();
756 * Update an authority item's workflow state.
757 * @param existingContext
762 * @throws DocumentReferenceException
764 public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext existingContext,
765 String parentIdentifier,
766 String itemIdentifier,
768 boolean updateRevNumber) throws DocumentReferenceException {
769 PoxPayloadOut result = null;
773 // We need CSIDs for both the parent authority and the authority item
775 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
776 String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
779 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
781 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(),
782 WorkflowClient.SERVICE_COMMONPART_NAME);
783 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
784 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
785 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
788 // Create a service context and document handler for the target resource -not the workflow resource itself.
790 ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
791 AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
792 targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
793 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
795 // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
797 String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
798 ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
800 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
801 TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
802 if (transitionDef == null) {
803 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
804 itemIdentifier, transition));
806 ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
808 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
809 getRepositoryClient(ctx).update(ctx, itemCsid, handler);
810 result = ctx.getOutput();
811 } catch (DocumentReferenceException de) {
813 } catch (Exception e) {
814 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
820 private PoxPayloadOut getAuthorityItem(
822 String parentIdentifier,
823 String itemIdentifier) throws Exception {
824 PoxPayloadOut result = null;
826 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
827 // We omit the parentShortId, only needed when doing a create...
828 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
830 Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
831 if (itemSpec.form == SpecifierForm.CSID) {
832 // TODO should we assert that the item is in the passed vocab?
833 getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
835 String itemWhereClause =
836 RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
837 DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
838 handler.setDocumentFilter(myFilter);
839 getRepositoryClient(ctx).get(ctx, handler);
842 result = (PoxPayloadOut) ctx.getOutput();
843 if (result != null) {
844 String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
845 if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
846 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
847 itemSpec.value, inAuthority, parentcsid));
854 public PoxPayloadOut getAuthorityItemWithExistingContext(
855 ServiceContext existingCtx,
856 String parentIdentifier,
857 String itemIdentifier) throws Exception {
858 PoxPayloadOut result = null;
860 ServiceContext ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
861 if (existingCtx.getCurrentRepositorySession() != null) {
862 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
863 ctx.setProperties(existingCtx.getProperties());
865 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
871 * Gets the authority item.
873 * @param parentspecifier either a CSID or one of the urn forms
874 * @param itemspecifier either a CSID or one of the urn forms
876 * @return the authority item
879 @Path("{csid}/items/{itemcsid}")
880 public byte[] getAuthorityItem(
881 @Context Request request,
882 @Context UriInfo uriInfo,
883 @Context ResourceMap resourceMap,
884 @PathParam("csid") String parentIdentifier,
885 @PathParam("itemcsid") String itemIdentifier) {
886 uriInfo = new UriInfoWrapper(uriInfo);
887 PoxPayloadOut result = null;
889 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx =
890 (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
892 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
893 ctx.setJaxRsContext(jaxRsContext);
895 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
896 } catch (DocumentNotFoundException dnf) {
897 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
898 } catch (Exception e) {
899 throw bigReThrow(e, ServiceMessages.GET_FAILED);
902 return result.getBytes();
906 * Most of the authority child classes will/should use this implementation. However, the Vocabulary service's item schema is
907 * different enough that it will have to override this method in it's resource class.
910 protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
911 String result = null;
913 result = NuxeoUtils.getPrimaryElPathPropertyName(
914 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
915 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
921 protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
922 String result = null;
924 result = NuxeoUtils.getMultiElPathPropertyName(
925 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
926 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
932 * Gets the authorityItem list for the specified authority
933 * If partialPerm is specified, keywords will be ignored.
935 * @param authorityIdentifier either a CSID or one of the urn forms
936 * @param partialTerm if non-null, matches partial terms
937 * @param keywords if non-null, matches terms in the keyword index for items
938 * @param ui passed to include additional parameters, like pagination controls
941 public AbstractCommonList getAuthorityItemList(ServiceContext existingContext,
942 String authorityIdentifier,
943 UriInfo uriInfo) throws Exception {
944 AbstractCommonList result = null;
946 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
947 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
948 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
949 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
950 ctx.setProperties(existingContext.getProperties());
953 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
954 String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
955 String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
956 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
957 String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
959 // For the wildcard case, parentcsid is null, but docHandler will deal with this.
960 // We omit the parentShortId, only needed when doing a create...
961 String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
962 lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
963 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
964 createItemDocumentHandler(ctx, parentcsid, null);
966 DocumentFilter myFilter = handler.getDocumentFilter();
967 // If we are not wildcarding the parent, add a restriction
968 if (parentcsid != null) {
969 myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
970 + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
971 + "'" + parentcsid + "'",
972 IQueryManager.SEARCH_QUALIFIER_AND);
975 if (Tools.notBlank(termStatus)) {
976 // Start with the qualified termStatus field
977 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
978 + AuthorityItemJAXBSchema.TERM_STATUS;
979 String[] filterTerms = termStatus.trim().split("\\|");
980 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
981 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
984 result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
990 * Gets the authorityItem list for the specified authority
991 * If partialPerm is specified, keywords will be ignored.
993 * @param authorityIdentifier either a CSID or one of the urn forms
994 * @param partialTerm if non-null, matches partial terms
995 * @param keywords if non-null, matches terms in the keyword index for items
996 * @param ui passed to include additional parameters, like pagination controls
998 * @return the authorityItem list
1001 @Path("{csid}/items")
1002 @Produces("application/xml")
1003 public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
1004 @Context UriInfo uriInfo) {
1005 uriInfo = new UriInfoWrapper(uriInfo);
1006 AbstractCommonList result = null;
1009 result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);
1010 } catch (Exception e) {
1011 throw bigReThrow(e, ServiceMessages.LIST_FAILED);
1018 * @return the name of the property used to specify references for items in this type of
1019 * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
1020 * Some types (like Vocabulary) use a separate property.
1022 protected String getRefPropName() {
1023 return ServiceBindingUtils.AUTH_REF_PROP;
1027 * Gets the entities referencing this Authority item instance. The service type
1028 * can be passed as a query param "type", and must match a configured type
1029 * for the service bindings. If not set, the type defaults to
1030 * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1032 * @param parentspecifier either a CSID or one of the urn forms
1033 * @param itemspecifier either a CSID or one of the urn forms
1036 * @return the info for the referencing objects
1039 @Path("{csid}/items/{itemcsid}/refObjs")
1040 @Produces("application/xml")
1041 public AuthorityRefDocList getReferencingObjects(
1042 @PathParam("csid") String parentSpecifier,
1043 @PathParam("itemcsid") String itemSpecifier,
1044 @Context UriTemplateRegistry uriTemplateRegistry,
1045 @Context UriInfo uriInfo) {
1046 uriInfo = new UriInfoWrapper(uriInfo);
1047 AuthorityRefDocList authRefDocList = null;
1049 authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriTemplateRegistry, uriInfo);
1050 } catch (Exception e) {
1051 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1054 if (authRefDocList == null) {
1055 Response response = Response.status(Response.Status.NOT_FOUND).entity(
1056 "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1057 "text/plain").build();
1058 throw new CSWebApplicationException(response);
1060 return authRefDocList;
1063 public AuthorityRefDocList getReferencingObjects(
1064 ServiceContext existingContext,
1065 String parentspecifier,
1066 String itemspecifier,
1067 UriTemplateRegistry uriTemplateRegistry,
1068 UriInfo uriInfo) throws Exception {
1069 //uriInfo = new UriInfoWrapper(uriInfo);
1070 AuthorityRefDocList authRefDocList = null;
1072 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1073 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1075 // Merge parts of existing context with our new context
1077 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1078 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If one exists, use the existing repo session
1079 ctx.setProperties(existingContext.getProperties());
1082 String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1083 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1085 // Remove the "type" property from the query params
1086 List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1087 if (serviceTypes == null || serviceTypes.isEmpty()) {
1088 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1091 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1092 authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
1094 return authRefDocList;
1098 * Gets the authority terms used in the indicated Authority item.
1100 * @param parentspecifier either a CSID or one of the urn forms
1101 * @param itemspecifier either a CSID or one of the urn forms
1102 * @param ui passed to include additional parameters, like pagination controls
1104 * @return the authority refs for the Authority item.
1107 @Path("{csid}/items/{itemcsid}/authorityrefs")
1108 @Produces("application/xml")
1109 public AuthorityRefList getAuthorityItemAuthorityRefs(
1110 @PathParam("csid") String parentspecifier,
1111 @PathParam("itemcsid") String itemspecifier,
1112 @Context UriInfo uriInfo) {
1113 uriInfo = new UriInfoWrapper(uriInfo);
1114 AuthorityRefList authRefList = null;
1117 // Note that we have to create the service context for the Items, not the main service
1118 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1119 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1120 String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1121 // We omit the parentShortId, only needed when doing a create...
1122 DocumentModelHandler<?, AbstractCommonList> handler =
1123 (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1125 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1127 List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1128 authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1129 } catch (Exception e) {
1130 throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1137 * Synchronizes a local authority item with a share authority server (SAS) item.
1139 * @param parentIdentifier
1140 * @param itemIdentifier
1144 @SuppressWarnings("unchecked")
1145 private PoxPayloadOut synchronizeItem(
1147 String parentIdentifier,
1148 String itemIdentifier,
1149 boolean syncHierarchicalRelationships) throws Exception {
1150 PoxPayloadOut result = null;
1151 AuthorityItemSpecifier specifier;
1152 boolean neededSync = false;
1154 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1155 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1156 handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1157 handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1158 handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1159 // Create an authority item specifier
1160 Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1161 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1162 specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1164 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1165 if (neededSync == true) {
1166 result = (PoxPayloadOut) ctx.getOutput();
1173 * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1174 * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1175 * local authority. The parent context was created for the authority (parent) because the sync started there.
1176 * @param existingCtx
1177 * @param parentIdentifier
1178 * @param itemIdentifier
1182 public PoxPayloadOut synchronizeItemWithExistingContext(
1183 ServiceContext existingCtx,
1184 String parentIdentifier,
1185 String itemIdentifier,
1186 boolean syncHierarchicalRelationships
1187 ) throws Exception {
1188 PoxPayloadOut result = null;
1190 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1191 existingCtx.getResourceMap(),
1192 existingCtx.getUriInfo());
1193 if (existingCtx.getCurrentRepositorySession() != null) {
1194 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1197 result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1203 * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1205 * @param specifier either CSIDs and/or one of the urn forms
1207 * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1210 @Path("{csid}/items/{itemcsid}/sync")
1211 public byte[] synchronizeItem(
1212 @Context ResourceMap resourceMap,
1213 @Context UriInfo uriInfo,
1214 @PathParam("csid") String parentIdentifier,
1215 @PathParam("itemcsid") String itemIdentifier) {
1216 uriInfo = new UriInfoWrapper(uriInfo);
1218 boolean neededSync = false;
1219 PoxPayloadOut payloadOut = null;
1222 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1223 payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1224 if (payloadOut != null) {
1227 } catch (Exception e) {
1228 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1232 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
1234 if (neededSync == true) {
1235 result = payloadOut.getBytes();
1237 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1238 itemIdentifier).getBytes();
1239 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1240 throw new CSWebApplicationException(response);
1247 * Update authorityItem.
1249 * @param parentspecifier either a CSID or one of the urn forms
1250 * @param itemspecifier either a CSID or one of the urn forms
1252 * @return the multipart output
1255 @Path("{csid}/items/{itemcsid}")
1256 public byte[] updateAuthorityItem(
1257 @Context ResourceMap resourceMap,
1258 @Context UriInfo uriInfo,
1259 @PathParam("csid") String parentSpecifier,
1260 @PathParam("itemcsid") String itemSpecifier,
1261 String xmlPayload) {
1262 uriInfo = new UriInfoWrapper(uriInfo);
1263 PoxPayloadOut result = null;
1266 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1267 result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1268 AuthorityServiceUtils.UPDATE_REV, // passing TRUE so rev num increases, passing
1269 AuthorityServiceUtils.NO_CHANGE, // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1270 AuthorityServiceUtils.NO_CHANGE); // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1271 } catch (Exception e) {
1272 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1275 return result.getBytes();
1278 public PoxPayloadOut updateAuthorityItem(
1279 ServiceContext itemServiceCtx, // Ok to be null. Will be null on PUT calls, but not on sync calls
1280 ResourceMap resourceMap,
1282 String parentspecifier,
1283 String itemspecifier,
1284 PoxPayloadIn theUpdate,
1285 boolean shouldUpdateRevNumber,
1288 ) throws Exception {
1289 PoxPayloadOut result = null;
1291 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
1292 String parentcsid = csidAndShortId.CSID;
1293 String parentShortId = csidAndShortId.shortIdentifier;
1295 // If the itemServiceCtx context is not null, use it. Otherwise, create a new context
1297 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
1299 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
1301 ctx.setInput(theUpdate); // the update payload
1304 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
1306 // We omit the parentShortId, only needed when doing a create...
1307 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
1308 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1309 if (isProposed != null) {
1310 handler.setIsProposed(isProposed);
1312 if (isSASItem != null) {
1313 handler.setIsSASItem(isSASItem);
1315 getRepositoryClient(ctx).update(ctx, itemcsid, handler);
1316 result = ctx.getOutput();
1322 * Delete authorityItem.
1324 * @param parentIdentifier the parentcsid
1325 * @param itemIdentifier the itemcsid
1327 * @return the response
1330 @Path("{csid}/items/{itemcsid}")
1331 public Response deleteAuthorityItem(
1332 @Context UriInfo uriInfo,
1333 @PathParam("csid") String parentIdentifier,
1334 @PathParam("itemcsid") String itemIdentifier) {
1335 uriInfo = new UriInfoWrapper(uriInfo);
1336 Response result = null;
1338 ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1339 ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1340 if (logger.isDebugEnabled()) {
1341 logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1345 ServiceContext ctx = createServiceContext(getItemServiceName(), uriInfo);
1346 deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1347 result = Response.status(HttpResponseCodes.SC_OK).build();
1348 } catch (Exception e) {
1349 throw bigReThrow(e, ServiceMessages.DELETE_FAILED + " itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1357 * @param existingCtx
1358 * @param parentIdentifier
1359 * @param itemIdentifier
1362 @SuppressWarnings("rawtypes")
1363 public boolean deleteAuthorityItem(ServiceContext existingCtx,
1364 String parentIdentifier,
1365 String itemIdentifier,
1366 boolean shouldUpdateRevNumber
1367 ) throws Exception {
1368 boolean result = true;
1370 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1371 if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1372 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1373 ctx.setProperties(existingCtx.getProperties());
1376 String parentcsid = null;
1378 parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1379 } catch (DocumentNotFoundException de) {
1380 logger.warn(String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1381 parentIdentifier, itemIdentifier));
1383 String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1385 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1386 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1387 result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1393 @Path("{csid}/items/{itemcsid}/" + hierarchy)
1394 @Produces("application/xml")
1395 public String getHierarchy(
1396 @PathParam("csid") String parentIdentifier,
1397 @PathParam("itemcsid") String itemIdentifier,
1398 @Context UriInfo uriInfo) throws Exception {
1399 uriInfo = new UriInfoWrapper(uriInfo);
1400 String result = null;
1404 // 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...?
1406 String calledUri = uriInfo.getPath();
1407 String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1408 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1410 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1411 String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1413 String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1414 if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1415 result = Hierarchy.surface(ctx, itemcsid, uri);
1417 result = Hierarchy.dive(ctx, itemcsid, uri);
1419 } catch (Exception e) {
1420 throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1431 protected String getItemDocType(String tenantId) {
1432 return getDocType(tenantId, getItemServiceName());
1436 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1437 * for the current resource, for all tenants
1439 * @return a map of URI templates for the current resource, for all tenants
1442 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1443 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1444 super.getUriRegistryEntries();
1445 List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1446 for (String tenantId : tenantIds) {
1447 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1449 return uriRegistryEntriesMap;
1455 public ServiceDescription getDescription(ServiceContext ctx) {
1456 ServiceDescription result = super.getDescription(ctx);
1457 result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));