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;
44 import org.collectionspace.services.client.IClientQueryParams;
45 import org.collectionspace.services.client.IQueryManager;
46 import org.collectionspace.services.client.PoxPayloadIn;
47 import org.collectionspace.services.client.PoxPayloadOut;
48 import org.collectionspace.services.client.XmlTools;
49 import org.collectionspace.services.client.workflow.WorkflowClient;
50 import org.collectionspace.services.common.CSWebApplicationException;
51 import org.collectionspace.services.common.NuxeoBasedResource;
52 import org.collectionspace.services.common.ResourceMap;
53 import org.collectionspace.services.common.ServiceMain;
54 import org.collectionspace.services.common.ServiceMessages;
55 import org.collectionspace.services.common.StoredValuesUriTemplate;
56 import org.collectionspace.services.common.UriTemplateFactory;
57 import org.collectionspace.services.common.UriTemplateRegistry;
58 import org.collectionspace.services.common.UriTemplateRegistryKey;
59 import org.collectionspace.services.common.api.RefName;
60 import org.collectionspace.services.common.api.Tools;
61 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
62 import org.collectionspace.services.common.authorityref.AuthorityRefList;
63 import org.collectionspace.services.common.context.JaxRsContext;
64 import org.collectionspace.services.common.context.MultipartServiceContext;
65 import org.collectionspace.services.common.context.RemoteServiceContext;
66 import org.collectionspace.services.common.context.ServiceBindingUtils;
67 import org.collectionspace.services.common.context.ServiceContext;
68 import org.collectionspace.services.common.document.DocumentException;
69 import org.collectionspace.services.common.document.DocumentFilter;
70 import org.collectionspace.services.common.document.DocumentHandler;
71 import org.collectionspace.services.common.document.DocumentNotFoundException;
72 import org.collectionspace.services.common.document.DocumentReferenceException;
73 import org.collectionspace.services.common.document.DocumentWrapper;
74 import org.collectionspace.services.common.document.Hierarchy;
75 import org.collectionspace.services.common.query.QueryManager;
76 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
77 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
78 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
79 import org.collectionspace.services.config.ClientType;
80 import org.collectionspace.services.config.service.ServiceBindingType;
81 import org.collectionspace.services.jaxb.AbstractCommonList;
82 import org.collectionspace.services.lifecycle.TransitionDef;
83 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
84 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
85 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentFilter;
86 import org.collectionspace.services.nuxeo.client.java.RepositoryClientImpl;
87 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
88 import org.collectionspace.services.workflow.WorkflowCommon;
89 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
90 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
91 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
92 import org.collectionspace.services.description.ServiceDescription;
93 import org.jboss.resteasy.util.HttpResponseCodes;
94 import org.nuxeo.ecm.core.api.DocumentModel;
95 import org.nuxeo.ecm.core.api.DocumentModelList;
96 import org.slf4j.Logger;
97 import org.slf4j.LoggerFactory;
100 * The Class AuthorityResource.
103 @Consumes("application/xml")
104 @Produces("application/xml")
105 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
106 extends NuxeoBasedResource {
108 final static String SEARCH_TYPE_TERMSTATUS = "ts";
109 public final static String hierarchy = "hierarchy";
111 protected Class<AuthCommon> authCommonClass;
112 protected Class<?> resourceClass;
113 protected String authorityCommonSchemaName;
114 protected String authorityItemCommonSchemaName;
115 final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed? I see no references to it.
117 final static String FETCH_SHORT_ID = "_fetch_";
118 public final static String PARENT_WILDCARD = "_ALL_";
120 final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
123 * Instantiates a new Authority resource.
125 public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
126 String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
127 this.authCommonClass = authCommonClass;
128 this.resourceClass = resourceClass;
129 this.authorityCommonSchemaName = authorityCommonSchemaName;
130 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
133 public abstract String getItemServiceName();
135 public abstract String getItemTermInfoGroupXPathBase();
138 protected String getVersionString() {
139 return "$LastChangedRevision: 2617 $";
143 public Class<AuthCommon> getCommonPartClass() {
144 return authCommonClass;
148 * Creates the item document handler.
151 * @param inAuthority the in vocabulary
153 * @return the document handler
155 * @throws Exception the exception
157 protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
158 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
159 String inAuthority, String parentShortIdentifier)
161 String authorityRefNameBase;
162 AuthorityItemDocumentModelHandler<?> docHandler;
164 if (parentShortIdentifier == null) {
165 authorityRefNameBase = null;
167 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getServiceName());
168 if (parentShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
169 if (ctx.getCurrentRepositorySession() != null) {
170 parentCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
172 // Get from parent document
173 parentShortIdentifier = getAuthShortIdentifier(parentCtx, inAuthority);
175 authorityRefNameBase = buildAuthorityRefNameBase(parentCtx, parentShortIdentifier);
178 docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
179 ctx.getCommonPartLabel(getItemServiceName()),
181 // FIXME - Richard and Aron think the following three lines should
182 // be in the constructor for the AuthorityItemDocumentModelHandler
183 // because all three are required fields.
184 docHandler.setInAuthority(inAuthority);
185 docHandler.setAuthorityRefNameBase(authorityRefNameBase);
186 docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
190 public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
191 throws DocumentNotFoundException, DocumentException {
192 String shortIdentifier = null;
195 AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
196 shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
197 } catch (Exception e) {
198 if (logger.isDebugEnabled()) {
199 logger.debug("Caught exception ", e);
201 throw new DocumentException(e);
204 return shortIdentifier;
207 protected String buildAuthorityRefNameBase(
208 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
209 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
210 ctx.getServiceName(),
211 null, // Only use shortId form!!!
212 shortIdentifier, null);
213 return authority.toString();
216 public static class CsidAndShortIdentifier {
218 String shortIdentifier;
221 protected String lookupParentCSID(String parentspecifier, String method,
222 String op, UriInfo uriInfo) throws Exception {
223 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(NULL_CONTEXT,
224 parentspecifier, method, op, uriInfo);
225 return tempResult.CSID;
228 protected String lookupParentCSID(ServiceContext ctx, String parentspecifier, String method,
229 String op, UriInfo uriInfo) throws Exception {
230 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
231 parentspecifier, method, op, uriInfo);
232 return tempResult.CSID;
236 private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
237 ServiceContext existingCtx, // Ok to be null
238 String parentIdentifier,
243 CsidAndShortIdentifier result = new CsidAndShortIdentifier();
244 Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
247 String parentShortIdentifier;
248 if (parentSpec.form == SpecifierForm.CSID) {
249 parentShortIdentifier = null;
250 parentcsid = parentSpec.value;
251 // Uncomment when app layer is ready to integrate
252 // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
253 parentShortIdentifier = FETCH_SHORT_ID;
255 parentShortIdentifier = parentSpec.value;
256 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
257 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
258 CoreSessionInterface repoSession = null;
259 if (existingCtx != null) {
260 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession(); // We want to use the thread's current repo session
262 parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
265 result.CSID = parentcsid;
266 result.shortIdentifier = parentShortIdentifier;
271 public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemspecifier, String parentcsid, String method, String op)
275 Specifier itemSpec = Specifier.getSpecifier(itemspecifier, method, op);
276 if (itemSpec.form == SpecifierForm.CSID) {
277 itemcsid = itemSpec.value;
279 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
280 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
281 CoreSessionInterface repoSession = null;
282 if (existingContext != null) {
283 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession(); // We want to use the thread's current repo session
285 itemcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
292 * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then
293 * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
294 * Resource. They then call this method on that resource.
297 public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item)
298 throws Exception, DocumentNotFoundException {
302 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
303 // Ensure we have the right context.
304 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
306 // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
307 RepositoryClientImpl client = (RepositoryClientImpl)getRepositoryClient(ctx);
308 String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
310 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
311 ctx = createServiceContext(getItemServiceName());
312 DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
313 DocumentModel docModel = docWrapper.getWrappedObject();
319 public Response createAuthority(String xmlPayload) {
321 // 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
322 // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
323 // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
324 // the code that creates new authorities. The authority document model handler will first check for authorities with the same short id before
325 // trying to create a new authority.
327 synchronized(AuthorityResource.class) {
329 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
330 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
331 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
333 String csid = getRepositoryClient(ctx).create(ctx, handler);
334 UriBuilder path = UriBuilder.fromResource(resourceClass);
335 path.path("" + csid);
336 Response response = Response.created(path.build()).build();
338 } catch (Exception e) {
339 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
344 protected boolean supportsReplicating(String tenantId, String serviceName) {
345 boolean result = false;
347 ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
348 result = sb.isSupportsReplicating();
354 * Synchronizes the authority and its items/terms with a Shared Authority Server.
356 * @param specifier either a CSID or one of the urn forms
358 * @return the authority
362 public byte[] synchronize(
363 @Context Request request,
365 @PathParam("csid") String identifier) {
367 boolean neededSync = false;
368 PoxPayloadOut payloadOut = null;
372 // Prevent multiple SAS synchronizations from occurring simultaneously by synchronizing this method.
374 synchronized(AuthorityResource.class) {
376 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
378 * Make sure this authority service supports synchronization
380 if (supportsReplicating(ctx.getTenantId(), ctx.getServiceName()) == false) {
381 throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
383 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
384 specifier = Specifier.getSpecifier(identifier, "getAuthority", "GET");
385 handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
386 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
387 payloadOut = ctx.getOutput();
388 } catch (Exception e) {
389 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, identifier);
393 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
395 if (neededSync == true) {
396 result = payloadOut.getBytes();
398 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
399 specifier.value).getBytes();
400 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
401 throw new CSWebApplicationException(response);
409 * Gets the authority.
411 * @param specifier either a CSID or one of the urn forms
413 * @return the authority
419 @Context Request request,
421 @PathParam("csid") String specifier) {
422 PoxPayloadOut result = null;
424 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
425 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
427 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
428 if (spec.form == SpecifierForm.CSID) {
429 if (logger.isDebugEnabled()) {
430 logger.debug("getAuthority with csid=" + spec.value);
432 getRepositoryClient(ctx).get(ctx, spec.value, handler);
434 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
435 DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
436 handler.setDocumentFilter(myFilter);
437 getRepositoryClient(ctx).get(ctx, handler);
439 result = ctx.getOutput();
441 } catch (Exception e) {
442 throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
445 if (result == null) {
446 Response response = Response.status(Response.Status.NOT_FOUND).entity(
447 "Get failed, the requested Authority specifier:" + specifier + ": was not found.").type(
448 "text/plain").build();
449 throw new CSWebApplicationException(response);
452 return result.getBytes();
456 * Finds and populates the authority list.
460 * @return the authority list
463 @Produces("application/xml")
464 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.
465 AbstractCommonList result = null;
468 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
469 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
471 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
472 DocumentFilter myFilter = handler.getDocumentFilter();
473 // Need to make the default sort order for authority items
474 // be on the displayName field
475 String sortBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
476 if (sortBy == null || sortBy.isEmpty()) {
477 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
478 + AuthorityItemJAXBSchema.DISPLAY_NAME;
479 myFilter.setOrderByClause(qualifiedDisplayNameField);
481 String nameQ = queryParams.getFirst("refName");
483 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
485 getRepositoryClient(ctx).getFiltered(ctx, handler);
486 result = handler.getCommonPartList();
487 } catch (Exception e) {
488 throw bigReThrow(e, ServiceMessages.GET_FAILED);
495 * Overriding this methods to see if we should update the revision number during the update. We don't
496 * want to update the rev number of synchronization operations.
499 protected PoxPayloadOut update(String csid,
500 PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
501 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
503 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
504 Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
505 if (shouldUpdateRev != null) {
506 handler.setShouldUpdateRevNumber(shouldUpdateRev);
508 getRepositoryClient(ctx).update(ctx, csid, handler);
509 return ctx.getOutput();
515 * @param specifier the csid or id
517 * @return the multipart output
521 public byte[] updateAuthority(
522 @PathParam("csid") String specifier,
524 PoxPayloadOut result = null;
526 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
527 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
528 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
529 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
531 if (spec.form == SpecifierForm.CSID) {
534 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
535 csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
537 getRepositoryClient(ctx).update(ctx, csid, handler);
538 result = ctx.getOutput();
539 } catch (Exception e) {
540 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
542 return result.getBytes();
548 * @param csid the csid
550 * @return the response
555 public Response old_deleteAuthority(@PathParam("csid") String csid) {
556 if (logger.isDebugEnabled()) {
557 logger.debug("deleteAuthority with csid=" + csid);
560 ensureCSID(csid, ServiceMessages.DELETE_FAILED, "Authority.csid");
561 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
562 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
563 getRepositoryClient(ctx).delete(ctx, csid, handler);
564 return Response.status(HttpResponseCodes.SC_OK).build();
565 } catch (Exception e) {
566 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, csid);
573 * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
575 * @return the response
579 public Response deleteAuthority(
580 @Context Request request,
582 @PathParam("csid") String specifier) {
583 if (logger.isDebugEnabled()) {
584 logger.debug("deleteAuthority with specifier=" + specifier);
588 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
589 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
591 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
592 if (spec.form == SpecifierForm.CSID) {
593 if (logger.isDebugEnabled()) {
594 logger.debug("deleteAuthority with csid=" + spec.value);
596 ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
597 getRepositoryClient(ctx).delete(ctx, spec.value, handler);
599 if (logger.isDebugEnabled()) {
600 logger.debug("deleteAuthority with specifier=" + spec.value);
602 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
603 getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
606 return Response.status(HttpResponseCodes.SC_OK).build();
607 } catch (Exception e) {
608 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
615 * @param parentspecifier - ID of the container. Can be URN or CSID form
616 * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
617 * @param isProposed - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
621 protected Response createAuthorityItem(ServiceContext ctx, String parentIdentifier,
622 boolean shouldUpdateRevNumber,
624 boolean isSasItem) throws Exception {
625 Response result = null;
627 // Note: must have the parentShortId, to do the create.
628 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
629 AuthorityItemDocumentModelHandler handler =
630 (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
631 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
632 handler.setIsProposed(isProposed);
633 handler.setIsSASItem(isSasItem);
634 // Make the client call
635 String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
637 // Build the JAX-RS response
638 UriBuilder path = UriBuilder.fromResource(resourceClass);
639 path.path(parent.CSID + "/items/" + itemcsid);
640 result = Response.created(path.build()).build();
646 * Called with an existing context.
648 * @param parentIdentifier
653 public Response createAuthorityItemWithParentContext(ServiceContext parentCtx,
654 String parentIdentifier,
656 boolean shouldUpdateRevNumber,
658 boolean isSASItem) throws Exception {
659 Response result = null;
661 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
662 parentCtx.getResourceMap(), parentCtx.getUriInfo());
663 if (parentCtx.getCurrentRepositorySession() != null) {
664 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
666 result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
671 /*************************************************************************
672 * Create an AuthorityItem - this is a sub-resource of Authority
673 * @param specifier either a CSID or one of the urn forms
674 * @return Authority item response
675 *************************************************************************/
677 @Path("{csid}/items")
678 public Response createAuthorityItem(
679 @Context ResourceMap resourceMap,
680 @Context UriInfo uriInfo,
681 @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
683 Response result = null;
686 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
687 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
688 result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
689 AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
690 } catch (Exception e) {
691 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
698 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
699 public byte[] getItemWorkflow(
700 @PathParam("csid") String csid,
701 @PathParam("itemcsid") String itemcsid) {
702 PoxPayloadOut result = null;
705 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
706 String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
708 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
709 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
710 ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
711 getRepositoryClient(ctx).get(ctx, itemcsid, handler);
712 result = ctx.getOutput();
713 } catch (Exception e) {
714 throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
716 return result.getBytes();
719 //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
720 // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
722 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
723 public byte[] updateItemWorkflowWithTransition(
724 @Context UriInfo uriInfo,
725 @PathParam("csid") String parentIdentifier,
726 @PathParam("itemcsid") String itemIdentifier,
727 @PathParam("transition") String transition) {
728 PoxPayloadOut result = null;
731 ServiceContext ctx = createServiceContext(getItemServiceName(), uriInfo);
732 result = updateItemWorkflowWithTransition(ctx,
733 parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
734 } catch (Exception e) {
735 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
738 return result.getBytes();
742 * Update an authority item's workflow state.
743 * @param existingContext
748 * @throws DocumentReferenceException
750 public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext existingContext,
751 String parentIdentifier,
752 String itemIdentifier,
754 boolean updateRevNumber) throws DocumentReferenceException {
755 PoxPayloadOut result = null;
759 // We need CSIDs for both the parent authority and the authority item
761 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
762 String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
765 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
767 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(),
768 WorkflowClient.SERVICE_COMMONPART_NAME);
769 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
770 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
771 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
774 // Create a service context and document handler for the target resource -not the workflow resource itself.
776 ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
777 AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
778 targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
779 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
781 // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
783 String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
784 ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
786 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
787 TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
788 if (transitionDef == null) {
789 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
790 itemIdentifier, transition));
792 ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
794 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
795 getRepositoryClient(ctx).update(ctx, itemCsid, handler);
796 result = ctx.getOutput();
797 } catch (DocumentReferenceException de) {
799 } catch (Exception e) {
800 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
806 private PoxPayloadOut getAuthorityItem(
808 String parentIdentifier,
809 String itemIdentifier) throws Exception {
810 PoxPayloadOut result = null;
812 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
813 // We omit the parentShortId, only needed when doing a create...
814 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
816 Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
817 if (itemSpec.form == SpecifierForm.CSID) {
818 // TODO should we assert that the item is in the passed vocab?
819 getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
821 String itemWhereClause =
822 RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
823 DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
824 handler.setDocumentFilter(myFilter);
825 getRepositoryClient(ctx).get(ctx, handler);
828 result = (PoxPayloadOut) ctx.getOutput();
829 if (result != null) {
830 String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
831 if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
832 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
833 itemSpec.value, inAuthority, parentcsid));
840 public PoxPayloadOut getAuthorityItemWithExistingContext(
841 ServiceContext existingCtx,
842 String parentIdentifier,
843 String itemIdentifier) throws Exception {
844 PoxPayloadOut result = null;
846 ServiceContext ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
847 if (existingCtx.getCurrentRepositorySession() != null) {
848 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
849 ctx.setProperties(existingCtx.getProperties());
851 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
857 * Gets the authority item.
859 * @param parentspecifier either a CSID or one of the urn forms
860 * @param itemspecifier either a CSID or one of the urn forms
862 * @return the authority item
865 @Path("{csid}/items/{itemcsid}")
866 public byte[] getAuthorityItem(
867 @Context Request request,
868 @Context UriInfo uriInfo,
869 @Context ResourceMap resourceMap,
870 @PathParam("csid") String parentIdentifier,
871 @PathParam("itemcsid") String itemIdentifier) {
872 PoxPayloadOut result = null;
874 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx =
875 (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
877 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
878 ctx.setJaxRsContext(jaxRsContext);
880 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
881 } catch (DocumentNotFoundException dnf) {
882 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
883 } catch (Exception e) {
884 throw bigReThrow(e, ServiceMessages.GET_FAILED);
887 return result.getBytes();
891 * Most of the authority child classes will/should use this implementation. However, the Vocabulary service's item schema is
892 * different enough that it will have to override this method in it's resource class.
895 protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
896 String result = null;
898 result = NuxeoUtils.getPrimaryElPathPropertyName(
899 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
900 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
906 protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
907 String result = null;
909 result = NuxeoUtils.getMultiElPathPropertyName(
910 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
911 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
917 * Gets the authorityItem list for the specified authority
918 * If partialPerm is specified, keywords will be ignored.
920 * @param authorityIdentifier either a CSID or one of the urn forms
921 * @param partialTerm if non-null, matches partial terms
922 * @param keywords if non-null, matches terms in the keyword index for items
923 * @param ui passed to include additional parameters, like pagination controls
926 public AbstractCommonList getAuthorityItemList(ServiceContext existingContext,
927 String authorityIdentifier,
928 UriInfo uriInfo) throws Exception {
929 AbstractCommonList result = null;
931 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
932 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
933 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
934 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
935 ctx.setProperties(existingContext.getProperties());
938 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
939 String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
940 String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
941 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
942 String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
944 // For the wildcard case, parentcsid is null, but docHandler will deal with this.
945 // We omit the parentShortId, only needed when doing a create...
946 String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
947 lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
948 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
949 createItemDocumentHandler(ctx, parentcsid, null);
951 DocumentFilter myFilter = handler.getDocumentFilter();
952 // If we are not wildcarding the parent, add a restriction
953 if (parentcsid != null) {
954 myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
955 + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
956 + "'" + parentcsid + "'",
957 IQueryManager.SEARCH_QUALIFIER_AND);
960 if (Tools.notBlank(termStatus)) {
961 // Start with the qualified termStatus field
962 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
963 + AuthorityItemJAXBSchema.TERM_STATUS;
964 String[] filterTerms = termStatus.trim().split("\\|");
965 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
966 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
969 result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
975 * Gets the authorityItem list for the specified authority
976 * If partialPerm is specified, keywords will be ignored.
978 * @param authorityIdentifier either a CSID or one of the urn forms
979 * @param partialTerm if non-null, matches partial terms
980 * @param keywords if non-null, matches terms in the keyword index for items
981 * @param ui passed to include additional parameters, like pagination controls
983 * @return the authorityItem list
986 @Path("{csid}/items")
987 @Produces("application/xml")
988 public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
989 @Context UriInfo uriInfo) {
990 AbstractCommonList result = null;
993 result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);
994 } catch (Exception e) {
995 throw bigReThrow(e, ServiceMessages.LIST_FAILED);
1002 * @return the name of the property used to specify references for items in this type of
1003 * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
1004 * Some types (like Vocabulary) use a separate property.
1006 protected String getRefPropName() {
1007 return ServiceBindingUtils.AUTH_REF_PROP;
1011 * Gets the entities referencing this Authority item instance. The service type
1012 * can be passed as a query param "type", and must match a configured type
1013 * for the service bindings. If not set, the type defaults to
1014 * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1016 * @param parentspecifier either a CSID or one of the urn forms
1017 * @param itemspecifier either a CSID or one of the urn forms
1020 * @return the info for the referencing objects
1023 @Path("{csid}/items/{itemcsid}/refObjs")
1024 @Produces("application/xml")
1025 public AuthorityRefDocList getReferencingObjects(
1026 @PathParam("csid") String parentSpecifier,
1027 @PathParam("itemcsid") String itemSpecifier,
1028 @Context UriTemplateRegistry uriTemplateRegistry,
1029 @Context UriInfo uriInfo) {
1030 AuthorityRefDocList authRefDocList = null;
1032 authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriTemplateRegistry, uriInfo);
1033 } catch (Exception e) {
1034 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1037 if (authRefDocList == null) {
1038 Response response = Response.status(Response.Status.NOT_FOUND).entity(
1039 "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1040 "text/plain").build();
1041 throw new CSWebApplicationException(response);
1043 return authRefDocList;
1046 public AuthorityRefDocList getReferencingObjects(
1047 ServiceContext existingContext,
1048 String parentspecifier,
1049 String itemspecifier,
1050 UriTemplateRegistry uriTemplateRegistry,
1051 UriInfo uriInfo) throws Exception {
1052 AuthorityRefDocList authRefDocList = null;
1054 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1055 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1057 // Merge parts of existing context with our new context
1059 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1060 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If one exists, use the existing repo session
1061 ctx.setProperties(existingContext.getProperties());
1064 String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1065 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1067 List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1068 if (serviceTypes == null || serviceTypes.isEmpty()) {
1069 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1072 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1073 authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
1075 return authRefDocList;
1079 * Gets the authority terms used in the indicated Authority item.
1081 * @param parentspecifier either a CSID or one of the urn forms
1082 * @param itemspecifier either a CSID or one of the urn forms
1083 * @param ui passed to include additional parameters, like pagination controls
1085 * @return the authority refs for the Authority item.
1088 @Path("{csid}/items/{itemcsid}/authorityrefs")
1089 @Produces("application/xml")
1090 public AuthorityRefList getAuthorityItemAuthorityRefs(
1091 @PathParam("csid") String parentspecifier,
1092 @PathParam("itemcsid") String itemspecifier,
1093 @Context UriInfo uriInfo) {
1094 AuthorityRefList authRefList = null;
1097 // Note that we have to create the service context for the Items, not the main service
1098 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1099 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1100 String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1101 // We omit the parentShortId, only needed when doing a create...
1102 DocumentModelHandler<?, AbstractCommonList> handler =
1103 (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1105 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1107 List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1108 authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1109 } catch (Exception e) {
1110 throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1117 * Synchronizes a local authority item with a share authority server (SAS) item.
1119 * @param parentIdentifier
1120 * @param itemIdentifier
1124 @SuppressWarnings("unchecked")
1125 private PoxPayloadOut synchronizeItem(
1127 String parentIdentifier,
1128 String itemIdentifier,
1129 boolean syncHierarchicalRelationships) throws Exception {
1130 PoxPayloadOut result = null;
1131 AuthorityItemSpecifier specifier;
1132 boolean neededSync = false;
1134 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1135 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1136 handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1137 handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1138 handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1139 // Create an authority item specifier
1140 Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1141 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1142 specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1144 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1145 if (neededSync == true) {
1146 result = (PoxPayloadOut) ctx.getOutput();
1153 * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1154 * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1155 * local authority. The parent context was created for the authority (parent) because the sync started there.
1156 * @param existingCtx
1157 * @param parentIdentifier
1158 * @param itemIdentifier
1162 public PoxPayloadOut synchronizeItemWithExistingContext(
1163 ServiceContext existingCtx,
1164 String parentIdentifier,
1165 String itemIdentifier,
1166 boolean syncHierarchicalRelationships
1167 ) throws Exception {
1168 PoxPayloadOut result = null;
1170 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1171 existingCtx.getResourceMap(),
1172 existingCtx.getUriInfo());
1173 if (existingCtx.getCurrentRepositorySession() != null) {
1174 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1177 result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1183 * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1185 * @param specifier either CSIDs and/or one of the urn forms
1187 * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1190 @Path("{csid}/items/{itemcsid}/sync")
1191 public byte[] synchronizeItem(
1192 @Context ResourceMap resourceMap,
1193 @Context UriInfo uriInfo,
1194 @PathParam("csid") String parentIdentifier,
1195 @PathParam("itemcsid") String itemIdentifier) {
1197 boolean neededSync = false;
1198 PoxPayloadOut payloadOut = null;
1201 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1202 payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1203 if (payloadOut != null) {
1206 } catch (Exception e) {
1207 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1211 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
1213 if (neededSync == true) {
1214 result = payloadOut.getBytes();
1216 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1217 itemIdentifier).getBytes();
1218 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1219 throw new CSWebApplicationException(response);
1226 * Update authorityItem.
1228 * @param parentspecifier either a CSID or one of the urn forms
1229 * @param itemspecifier either a CSID or one of the urn forms
1231 * @return the multipart output
1234 @Path("{csid}/items/{itemcsid}")
1235 public byte[] updateAuthorityItem(
1236 @Context ResourceMap resourceMap,
1237 @Context UriInfo uriInfo,
1238 @PathParam("csid") String parentSpecifier,
1239 @PathParam("itemcsid") String itemSpecifier,
1240 String xmlPayload) {
1241 PoxPayloadOut result = null;
1244 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1245 result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1246 AuthorityServiceUtils.UPDATE_REV, // passing TRUE so rev num increases, passing
1247 AuthorityServiceUtils.NO_CHANGE, // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1248 AuthorityServiceUtils.NO_CHANGE); // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1249 } catch (Exception e) {
1250 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1253 return result.getBytes();
1256 public PoxPayloadOut updateAuthorityItem(
1257 ServiceContext itemServiceCtx, // Ok to be null. Will be null on PUT calls, but not on sync calls
1258 ResourceMap resourceMap,
1260 String parentspecifier,
1261 String itemspecifier,
1262 PoxPayloadIn theUpdate,
1263 boolean shouldUpdateRevNumber,
1266 ) throws Exception {
1267 PoxPayloadOut result = null;
1269 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
1270 String parentcsid = csidAndShortId.CSID;
1271 String parentShortId = csidAndShortId.shortIdentifier;
1273 // If the itemServiceCtx context is not null, use it. Otherwise, create a new context
1275 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
1277 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
1279 ctx.setInput(theUpdate); // the update payload
1282 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
1284 // We omit the parentShortId, only needed when doing a create...
1285 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
1286 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1287 if (isProposed != null) {
1288 handler.setIsProposed(isProposed);
1290 if (isSASItem != null) {
1291 handler.setIsSASItem(isSASItem);
1293 getRepositoryClient(ctx).update(ctx, itemcsid, handler);
1294 result = ctx.getOutput();
1300 * Delete authorityItem.
1302 * @param parentIdentifier the parentcsid
1303 * @param itemIdentifier the itemcsid
1305 * @return the response
1308 @Path("{csid}/items/{itemcsid}")
1309 public Response deleteAuthorityItem(
1310 @Context UriInfo uriInfo,
1311 @PathParam("csid") String parentIdentifier,
1312 @PathParam("itemcsid") String itemIdentifier) {
1313 Response result = null;
1315 ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1316 ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1317 if (logger.isDebugEnabled()) {
1318 logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1322 ServiceContext ctx = createServiceContext(getItemServiceName(), uriInfo);
1323 deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1324 result = Response.status(HttpResponseCodes.SC_OK).build();
1325 } catch (Exception e) {
1326 throw bigReThrow(e, ServiceMessages.DELETE_FAILED + " itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1334 * @param existingCtx
1335 * @param parentIdentifier
1336 * @param itemIdentifier
1339 @SuppressWarnings("rawtypes")
1340 public boolean deleteAuthorityItem(ServiceContext existingCtx,
1341 String parentIdentifier,
1342 String itemIdentifier,
1343 boolean shouldUpdateRevNumber
1344 ) throws Exception {
1345 boolean result = true;
1347 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1348 if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1349 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1350 ctx.setProperties(existingCtx.getProperties());
1353 String parentcsid = null;
1355 parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1356 } catch (DocumentNotFoundException de) {
1357 logger.warn(String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1358 parentIdentifier, itemIdentifier));
1360 String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1362 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1363 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1364 result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1370 @Path("{csid}/items/{itemcsid}/" + hierarchy)
1371 @Produces("application/xml")
1372 public String getHierarchy(
1373 @PathParam("csid") String parentIdentifier,
1374 @PathParam("itemcsid") String itemIdentifier,
1375 @Context UriInfo uriInfo) throws Exception {
1376 String result = null;
1380 // 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...?
1382 String calledUri = uriInfo.getPath();
1383 String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1384 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1386 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1387 String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1389 String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1390 if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1391 result = Hierarchy.surface(ctx, itemcsid, uri);
1393 result = Hierarchy.dive(ctx, itemcsid, uri);
1395 } catch (Exception e) {
1396 throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1407 protected String getItemDocType(String tenantId) {
1408 return getDocType(tenantId, getItemServiceName());
1412 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1413 * for the current resource, for all tenants
1415 * @return a map of URI templates for the current resource, for all tenants
1418 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1419 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1420 super.getUriRegistryEntries();
1421 List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1422 for (String tenantId : tenantIds) {
1423 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1425 return uriRegistryEntriesMap;
1431 public ServiceDescription getDescription(ServiceContext ctx) {
1432 ServiceDescription result = super.getDescription(ctx);
1433 result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));