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.jboss.resteasy.util.HttpResponseCodes;
93 import org.nuxeo.ecm.core.api.DocumentModel;
94 import org.nuxeo.ecm.core.api.DocumentModelList;
95 import org.slf4j.Logger;
96 import org.slf4j.LoggerFactory;
99 * The Class AuthorityResource.
102 @Consumes("application/xml")
103 @Produces("application/xml")
104 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
105 extends NuxeoBasedResource {
107 final static String SEARCH_TYPE_TERMSTATUS = "ts";
108 public final static String hierarchy = "hierarchy";
110 protected Class<AuthCommon> authCommonClass;
111 protected Class<?> resourceClass;
112 protected String authorityCommonSchemaName;
113 protected String authorityItemCommonSchemaName;
114 final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed? I see no references to it.
116 final static String FETCH_SHORT_ID = "_fetch_";
117 public final static String PARENT_WILDCARD = "_ALL_";
119 final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
122 * Instantiates a new Authority resource.
124 public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
125 String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
126 this.authCommonClass = authCommonClass;
127 this.resourceClass = resourceClass;
128 this.authorityCommonSchemaName = authorityCommonSchemaName;
129 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
132 public abstract String getItemServiceName();
134 public abstract String getItemTermInfoGroupXPathBase();
137 protected String getVersionString() {
138 return "$LastChangedRevision: 2617 $";
142 public Class<AuthCommon> getCommonPartClass() {
143 return authCommonClass;
147 * Creates the item document handler.
150 * @param inAuthority the in vocabulary
152 * @return the document handler
154 * @throws Exception the exception
156 protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
157 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
158 String inAuthority, String parentShortIdentifier)
160 String authorityRefNameBase;
161 AuthorityItemDocumentModelHandler<?> docHandler;
163 if (parentShortIdentifier == null) {
164 authorityRefNameBase = null;
166 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getServiceName());
167 if (parentShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
168 if (ctx.getCurrentRepositorySession() != null) {
169 parentCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
171 // Get from parent document
172 parentShortIdentifier = getAuthShortIdentifier(parentCtx, inAuthority);
174 authorityRefNameBase = buildAuthorityRefNameBase(parentCtx, parentShortIdentifier);
177 docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
178 ctx.getCommonPartLabel(getItemServiceName()),
180 // FIXME - Richard and Aron think the following three lines should
181 // be in the constructor for the AuthorityItemDocumentModelHandler
182 // because all three are required fields.
183 docHandler.setInAuthority(inAuthority);
184 docHandler.setAuthorityRefNameBase(authorityRefNameBase);
185 docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
189 public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
190 throws DocumentNotFoundException, DocumentException {
191 String shortIdentifier = null;
194 AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
195 shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
196 } catch (Exception e) {
197 if (logger.isDebugEnabled()) {
198 logger.debug("Caught exception ", e);
200 throw new DocumentException(e);
203 return shortIdentifier;
206 protected String buildAuthorityRefNameBase(
207 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
208 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
209 ctx.getServiceName(),
210 null, // Only use shortId form!!!
211 shortIdentifier, null);
212 return authority.toString();
215 public static class CsidAndShortIdentifier {
217 String shortIdentifier;
220 protected String lookupParentCSID(String parentspecifier, String method,
221 String op, UriInfo uriInfo) throws Exception {
222 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(NULL_CONTEXT,
223 parentspecifier, method, op, uriInfo);
224 return tempResult.CSID;
227 protected String lookupParentCSID(ServiceContext ctx, String parentspecifier, String method,
228 String op, UriInfo uriInfo) throws Exception {
229 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
230 parentspecifier, method, op, uriInfo);
231 return tempResult.CSID;
235 private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
236 ServiceContext existingCtx, // Ok to be null
237 String parentIdentifier,
242 CsidAndShortIdentifier result = new CsidAndShortIdentifier();
243 Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
246 String parentShortIdentifier;
247 if (parentSpec.form == SpecifierForm.CSID) {
248 parentShortIdentifier = null;
249 parentcsid = parentSpec.value;
250 // Uncomment when app layer is ready to integrate
251 // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
252 parentShortIdentifier = FETCH_SHORT_ID;
254 parentShortIdentifier = parentSpec.value;
255 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
256 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
257 CoreSessionInterface repoSession = null;
258 if (existingCtx != null) {
259 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession(); // We want to use the thread's current repo session
261 parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
264 result.CSID = parentcsid;
265 result.shortIdentifier = parentShortIdentifier;
270 public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemspecifier, String parentcsid, String method, String op)
274 Specifier itemSpec = Specifier.getSpecifier(itemspecifier, method, op);
275 if (itemSpec.form == SpecifierForm.CSID) {
276 itemcsid = itemSpec.value;
278 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
279 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
280 CoreSessionInterface repoSession = null;
281 if (existingContext != null) {
282 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession(); // We want to use the thread's current repo session
284 itemcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
291 * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then
292 * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
293 * Resource. They then call this method on that resource.
296 public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item)
297 throws Exception, DocumentNotFoundException {
301 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
302 // Ensure we have the right context.
303 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
305 // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
306 RepositoryClientImpl client = (RepositoryClientImpl)getRepositoryClient(ctx);
307 String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
309 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
310 ctx = createServiceContext(getItemServiceName());
311 DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
312 DocumentModel docModel = docWrapper.getWrappedObject();
318 public Response createAuthority(String xmlPayload) {
320 // 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
321 // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
322 // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
323 // the code that creates new authorities. The authority document model handler will first check for authorities with the same short id before
324 // trying to create a new authority.
326 synchronized(AuthorityResource.class) {
328 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
329 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
330 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
332 String csid = getRepositoryClient(ctx).create(ctx, handler);
333 UriBuilder path = UriBuilder.fromResource(resourceClass);
334 path.path("" + csid);
335 Response response = Response.created(path.build()).build();
337 } catch (Exception e) {
338 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
343 protected boolean supportsSync(String tenantId, String serviceName) {
344 boolean result = false;
346 ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
347 result = sb.isSupportsSynchronization();
353 * Synchronizes the authority and its terms with a Shared Authority Server.
355 * @param specifier either a CSID or one of the urn forms
357 * @return the authority
361 public byte[] synchronize(
362 @Context Request request,
364 @PathParam("csid") String identifier) {
366 boolean neededSync = false;
367 PoxPayloadOut payloadOut = null;
371 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
373 * Make sure this authority service supports synchronization
375 if (supportsSync(ctx.getTenantId(), ctx.getServiceName()) == false) {
376 throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
378 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
379 specifier = Specifier.getSpecifier(identifier, "getAuthority", "GET");
380 handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
381 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
382 payloadOut = ctx.getOutput();
383 } catch (Exception e) {
384 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, identifier);
388 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
390 if (neededSync == true) {
391 result = payloadOut.getBytes();
393 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
394 specifier.value).getBytes();
395 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
396 throw new CSWebApplicationException(response);
402 * Gets the authority.
404 * @param specifier either a CSID or one of the urn forms
406 * @return the authority
412 @Context Request request,
414 @PathParam("csid") String specifier) {
415 PoxPayloadOut result = null;
417 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
418 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
420 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
421 if (spec.form == SpecifierForm.CSID) {
422 if (logger.isDebugEnabled()) {
423 logger.debug("getAuthority with csid=" + spec.value);
425 getRepositoryClient(ctx).get(ctx, spec.value, handler);
427 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
428 DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
429 handler.setDocumentFilter(myFilter);
430 getRepositoryClient(ctx).get(ctx, handler);
432 result = ctx.getOutput();
434 } catch (Exception e) {
435 throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
438 if (result == null) {
439 Response response = Response.status(Response.Status.NOT_FOUND).entity(
440 "Get failed, the requested Authority specifier:" + specifier + ": was not found.").type(
441 "text/plain").build();
442 throw new CSWebApplicationException(response);
445 return result.getBytes();
449 * Finds and populates the authority list.
453 * @return the authority list
456 @Produces("application/xml")
457 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.
458 AbstractCommonList result = null;
461 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
462 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
464 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
465 DocumentFilter myFilter = handler.getDocumentFilter();
466 // Need to make the default sort order for authority items
467 // be on the displayName field
468 String sortBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
469 if (sortBy == null || sortBy.isEmpty()) {
470 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
471 + AuthorityItemJAXBSchema.DISPLAY_NAME;
472 myFilter.setOrderByClause(qualifiedDisplayNameField);
474 String nameQ = queryParams.getFirst("refName");
476 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
478 getRepositoryClient(ctx).getFiltered(ctx, handler);
479 result = handler.getCommonPartList();
480 } catch (Exception e) {
481 throw bigReThrow(e, ServiceMessages.GET_FAILED);
488 * Overriding this methods to see if we should update the revision number during the update. We don't
489 * want to update the rev number of synchronization operations.
492 protected PoxPayloadOut update(String csid,
493 PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
494 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
496 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
497 Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
498 if (shouldUpdateRev != null) {
499 handler.setShouldUpdateRevNumber(shouldUpdateRev);
501 getRepositoryClient(ctx).update(ctx, csid, handler);
502 return ctx.getOutput();
508 * @param specifier the csid or id
510 * @return the multipart output
514 public byte[] updateAuthority(
515 @PathParam("csid") String specifier,
517 PoxPayloadOut result = null;
519 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
520 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
521 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
522 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
524 if (spec.form == SpecifierForm.CSID) {
527 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
528 csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
530 getRepositoryClient(ctx).update(ctx, csid, handler);
531 result = ctx.getOutput();
532 } catch (Exception e) {
533 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
535 return result.getBytes();
541 * @param csid the csid
543 * @return the response
548 public Response old_deleteAuthority(@PathParam("csid") String csid) {
549 if (logger.isDebugEnabled()) {
550 logger.debug("deleteAuthority with csid=" + csid);
553 ensureCSID(csid, ServiceMessages.DELETE_FAILED, "Authority.csid");
554 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
555 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
556 getRepositoryClient(ctx).delete(ctx, csid, handler);
557 return Response.status(HttpResponseCodes.SC_OK).build();
558 } catch (Exception e) {
559 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, csid);
566 * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
568 * @return the response
572 public Response deleteAuthority(
573 @Context Request request,
575 @PathParam("csid") String specifier) {
576 if (logger.isDebugEnabled()) {
577 logger.debug("deleteAuthority with specifier=" + specifier);
581 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
582 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
584 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
585 if (spec.form == SpecifierForm.CSID) {
586 if (logger.isDebugEnabled()) {
587 logger.debug("deleteAuthority with csid=" + spec.value);
589 ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
590 getRepositoryClient(ctx).delete(ctx, spec.value, handler);
592 if (logger.isDebugEnabled()) {
593 logger.debug("deleteAuthority with specifier=" + spec.value);
595 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
596 getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
599 return Response.status(HttpResponseCodes.SC_OK).build();
600 } catch (Exception e) {
601 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
608 * @param parentspecifier - ID of the container. Can be URN or CSID form
609 * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
610 * @param isProposed - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
614 protected Response createAuthorityItem(ServiceContext ctx, String parentIdentifier,
615 boolean shouldUpdateRevNumber,
617 boolean isSasItem) throws Exception {
618 Response result = null;
620 // Note: must have the parentShortId, to do the create.
621 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
622 AuthorityItemDocumentModelHandler handler =
623 (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
624 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
625 handler.setIsProposed(isProposed);
626 handler.setIsSASItem(isSasItem);
627 // Make the client call
628 String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
630 // Build the JAX-RS response
631 UriBuilder path = UriBuilder.fromResource(resourceClass);
632 path.path(parent.CSID + "/items/" + itemcsid);
633 result = Response.created(path.build()).build();
639 * Called with an existing context.
641 * @param parentIdentifier
646 public Response createAuthorityItemWithParentContext(ServiceContext parentCtx,
647 String parentIdentifier,
649 boolean shouldUpdateRevNumber,
651 boolean isSASItem) throws Exception {
652 Response result = null;
654 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
655 parentCtx.getResourceMap(), parentCtx.getUriInfo());
656 if (parentCtx.getCurrentRepositorySession() != null) {
657 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
659 result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
664 /*************************************************************************
665 * Create an AuthorityItem - this is a sub-resource of Authority
666 * @param specifier either a CSID or one of the urn forms
667 * @return Authority item response
668 *************************************************************************/
670 @Path("{csid}/items")
671 public Response createAuthorityItem(
672 @Context ResourceMap resourceMap,
673 @Context UriInfo uriInfo,
674 @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
676 Response result = null;
679 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
680 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
681 result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
682 AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
683 } catch (Exception e) {
684 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
691 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
692 public byte[] getItemWorkflow(
693 @PathParam("csid") String csid,
694 @PathParam("itemcsid") String itemcsid) {
695 PoxPayloadOut result = null;
698 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
699 String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
701 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
702 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
703 ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
704 getRepositoryClient(ctx).get(ctx, itemcsid, handler);
705 result = ctx.getOutput();
706 } catch (Exception e) {
707 throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
709 return result.getBytes();
712 //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
713 // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
715 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
716 public byte[] updateItemWorkflowWithTransition(
717 @Context UriInfo uriInfo,
718 @PathParam("csid") String parentIdentifier,
719 @PathParam("itemcsid") String itemIdentifier,
720 @PathParam("transition") String transition) {
721 PoxPayloadOut result = null;
724 ServiceContext ctx = createServiceContext(getItemServiceName(), uriInfo);
725 result = updateItemWorkflowWithTransition(ctx,
726 parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
727 } catch (Exception e) {
728 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
731 return result.getBytes();
735 * Update an authority item's workflow state.
736 * @param existingContext
741 * @throws DocumentReferenceException
743 public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext existingContext,
744 String parentIdentifier,
745 String itemIdentifier,
747 boolean updateRevNumber) throws DocumentReferenceException {
748 PoxPayloadOut result = null;
752 // We need CSIDs for both the parent authority and the authority item
754 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
755 String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
758 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
760 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(),
761 WorkflowClient.SERVICE_COMMONPART_NAME);
762 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
763 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
764 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
767 // Create a service context and document handler for the target resource -not the workflow resource itself.
769 ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
770 AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
771 targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
772 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
774 // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
776 String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
777 ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
779 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
780 TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
781 if (transitionDef == null) {
782 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
783 itemIdentifier, transition));
785 ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
787 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
788 getRepositoryClient(ctx).update(ctx, itemCsid, handler);
789 result = ctx.getOutput();
790 } catch (DocumentReferenceException de) {
792 } catch (Exception e) {
793 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
799 private PoxPayloadOut getAuthorityItem(
801 String parentIdentifier,
802 String itemIdentifier) throws Exception {
803 PoxPayloadOut result = null;
805 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
806 // We omit the parentShortId, only needed when doing a create...
807 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
809 Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
810 if (itemSpec.form == SpecifierForm.CSID) {
811 // TODO should we assert that the item is in the passed vocab?
812 getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
814 String itemWhereClause =
815 RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
816 DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
817 handler.setDocumentFilter(myFilter);
818 getRepositoryClient(ctx).get(ctx, handler);
821 result = (PoxPayloadOut) ctx.getOutput();
822 if (result != null) {
823 String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
824 if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
825 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
826 itemSpec.value, inAuthority, parentcsid));
833 public PoxPayloadOut getAuthorityItemWithExistingContext(
834 ServiceContext existingCtx,
835 String parentIdentifier,
836 String itemIdentifier) throws Exception {
837 PoxPayloadOut result = null;
839 ServiceContext ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
840 if (existingCtx.getCurrentRepositorySession() != null) {
841 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
842 ctx.setProperties(existingCtx.getProperties());
844 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
850 * Gets the authority item.
852 * @param parentspecifier either a CSID or one of the urn forms
853 * @param itemspecifier either a CSID or one of the urn forms
855 * @return the authority item
858 @Path("{csid}/items/{itemcsid}")
859 public byte[] getAuthorityItem(
860 @Context Request request,
861 @Context UriInfo uriInfo,
862 @Context ResourceMap resourceMap,
863 @PathParam("csid") String parentIdentifier,
864 @PathParam("itemcsid") String itemIdentifier) {
865 PoxPayloadOut result = null;
867 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx =
868 (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
870 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
871 ctx.setJaxRsContext(jaxRsContext);
873 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
874 } catch (DocumentNotFoundException dnf) {
875 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
876 } catch (Exception e) {
877 throw bigReThrow(e, ServiceMessages.GET_FAILED);
880 return result.getBytes();
884 * Most of the authority child classes will/should use this implementation. However, the Vocabulary service's item schema is
885 * different enough that it will have to override this method in it's resource class.
888 protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
889 String result = null;
891 result = NuxeoUtils.getPrimaryElPathPropertyName(
892 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
893 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
899 protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
900 String result = null;
902 result = NuxeoUtils.getMultiElPathPropertyName(
903 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
904 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
910 * Gets the authorityItem list for the specified authority
911 * If partialPerm is specified, keywords will be ignored.
913 * @param authorityIdentifier either a CSID or one of the urn forms
914 * @param partialTerm if non-null, matches partial terms
915 * @param keywords if non-null, matches terms in the keyword index for items
916 * @param ui passed to include additional parameters, like pagination controls
919 public AbstractCommonList getAuthorityItemList(ServiceContext existingContext,
920 String authorityIdentifier,
921 UriInfo uriInfo) throws Exception {
922 AbstractCommonList result = null;
924 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
925 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
926 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
927 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
928 ctx.setProperties(existingContext.getProperties());
931 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
932 String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
933 String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
934 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
935 String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
937 // For the wildcard case, parentcsid is null, but docHandler will deal with this.
938 // We omit the parentShortId, only needed when doing a create...
939 String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
940 lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
941 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
942 createItemDocumentHandler(ctx, parentcsid, null);
944 DocumentFilter myFilter = handler.getDocumentFilter();
945 // If we are not wildcarding the parent, add a restriction
946 if (parentcsid != null) {
947 myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
948 + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
949 + "'" + parentcsid + "'",
950 IQueryManager.SEARCH_QUALIFIER_AND);
953 if (Tools.notBlank(termStatus)) {
954 // Start with the qualified termStatus field
955 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
956 + AuthorityItemJAXBSchema.TERM_STATUS;
957 String[] filterTerms = termStatus.trim().split("\\|");
958 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
959 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
962 result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
968 * Gets the authorityItem list for the specified authority
969 * If partialPerm is specified, keywords will be ignored.
971 * @param authorityIdentifier either a CSID or one of the urn forms
972 * @param partialTerm if non-null, matches partial terms
973 * @param keywords if non-null, matches terms in the keyword index for items
974 * @param ui passed to include additional parameters, like pagination controls
976 * @return the authorityItem list
979 @Path("{csid}/items")
980 @Produces("application/xml")
981 public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
982 @Context UriInfo uriInfo) {
983 AbstractCommonList result = null;
986 result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);
987 } catch (Exception e) {
988 throw bigReThrow(e, ServiceMessages.LIST_FAILED);
995 * @return the name of the property used to specify references for items in this type of
996 * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
997 * Some types (like Vocabulary) use a separate property.
999 protected String getRefPropName() {
1000 return ServiceBindingUtils.AUTH_REF_PROP;
1004 * Gets the entities referencing this Authority item instance. The service type
1005 * can be passed as a query param "type", and must match a configured type
1006 * for the service bindings. If not set, the type defaults to
1007 * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1009 * @param parentspecifier either a CSID or one of the urn forms
1010 * @param itemspecifier either a CSID or one of the urn forms
1013 * @return the info for the referencing objects
1016 @Path("{csid}/items/{itemcsid}/refObjs")
1017 @Produces("application/xml")
1018 public AuthorityRefDocList getReferencingObjects(
1019 @PathParam("csid") String parentSpecifier,
1020 @PathParam("itemcsid") String itemSpecifier,
1021 @Context UriTemplateRegistry uriTemplateRegistry,
1022 @Context UriInfo uriInfo) {
1023 AuthorityRefDocList authRefDocList = null;
1025 authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriTemplateRegistry, uriInfo);
1026 } catch (Exception e) {
1027 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1030 if (authRefDocList == null) {
1031 Response response = Response.status(Response.Status.NOT_FOUND).entity(
1032 "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1033 "text/plain").build();
1034 throw new CSWebApplicationException(response);
1036 return authRefDocList;
1039 public AuthorityRefDocList getReferencingObjects(
1040 ServiceContext existingContext,
1041 String parentspecifier,
1042 String itemspecifier,
1043 UriTemplateRegistry uriTemplateRegistry,
1044 UriInfo uriInfo) throws Exception {
1045 AuthorityRefDocList authRefDocList = null;
1047 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1048 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1050 // Merge parts of existing context with our new context
1052 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1053 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If one exists, use the existing repo session
1054 ctx.setProperties(existingContext.getProperties());
1057 String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1058 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1060 List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1061 if (serviceTypes == null || serviceTypes.isEmpty()) {
1062 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1065 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1066 authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
1068 return authRefDocList;
1072 * Gets the authority terms used in the indicated Authority item.
1074 * @param parentspecifier either a CSID or one of the urn forms
1075 * @param itemspecifier either a CSID or one of the urn forms
1076 * @param ui passed to include additional parameters, like pagination controls
1078 * @return the authority refs for the Authority item.
1081 @Path("{csid}/items/{itemcsid}/authorityrefs")
1082 @Produces("application/xml")
1083 public AuthorityRefList getAuthorityItemAuthorityRefs(
1084 @PathParam("csid") String parentspecifier,
1085 @PathParam("itemcsid") String itemspecifier,
1086 @Context UriInfo uriInfo) {
1087 AuthorityRefList authRefList = null;
1090 // Note that we have to create the service context for the Items, not the main service
1091 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1092 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1093 String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1094 // We omit the parentShortId, only needed when doing a create...
1095 DocumentModelHandler<?, AbstractCommonList> handler =
1096 (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1098 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1100 List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1101 authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1102 } catch (Exception e) {
1103 throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1110 * Synchronizes a local authority item with a share authority server (SAS) item.
1112 * @param parentIdentifier
1113 * @param itemIdentifier
1117 private PoxPayloadOut synchronizeItem(
1119 String parentIdentifier,
1120 String itemIdentifier) throws Exception {
1121 PoxPayloadOut result = null;
1122 AuthorityItemSpecifier specifier;
1123 boolean neededSync = false;
1125 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1126 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1127 handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1128 handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1129 // Create an authority item specifier
1130 Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1131 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1132 specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1134 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1135 if (neededSync == true) {
1136 result = (PoxPayloadOut) ctx.getOutput();
1143 * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1144 * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1145 * local authority. The parent context was created for the authority (parent) because the sync started there.
1146 * @param existingCtx
1147 * @param parentIdentifier
1148 * @param itemIdentifier
1152 public PoxPayloadOut synchronizeItemWithExistingContext(
1153 ServiceContext existingCtx,
1154 String parentIdentifier,
1155 String itemIdentifier
1156 ) throws Exception {
1157 PoxPayloadOut result = null;
1159 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1160 existingCtx.getResourceMap(),
1161 existingCtx.getUriInfo());
1162 if (existingCtx.getCurrentRepositorySession() != null) {
1163 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1165 result = synchronizeItem(ctx, parentIdentifier, itemIdentifier);
1171 * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1173 * @param specifier either CSIDs and/or one of the urn forms
1175 * @return the authority item if it was synchronized with SAS
1178 @Path("{csid}/items/{itemcsid}/sync")
1179 public byte[] synchronizeItem(
1180 @Context ResourceMap resourceMap,
1181 @Context UriInfo uriInfo,
1182 @PathParam("csid") String parentIdentifier,
1183 @PathParam("itemcsid") String itemIdentifier) {
1185 boolean neededSync = false;
1186 PoxPayloadOut payloadOut = null;
1189 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1190 payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier);
1191 if (payloadOut != null) {
1194 } catch (Exception e) {
1195 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1199 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
1201 if (neededSync == true) {
1202 result = payloadOut.getBytes();
1204 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1205 itemIdentifier).getBytes();
1206 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1207 throw new CSWebApplicationException(response);
1214 * Update authorityItem.
1216 * @param parentspecifier either a CSID or one of the urn forms
1217 * @param itemspecifier either a CSID or one of the urn forms
1219 * @return the multipart output
1222 @Path("{csid}/items/{itemcsid}")
1223 public byte[] updateAuthorityItem(
1224 @Context ResourceMap resourceMap,
1225 @Context UriInfo uriInfo,
1226 @PathParam("csid") String parentSpecifier,
1227 @PathParam("itemcsid") String itemSpecifier,
1228 String xmlPayload) {
1229 PoxPayloadOut result = null;
1232 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1233 result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1234 AuthorityServiceUtils.UPDATE_REV, // passing TRUE so rev num increases, passing
1235 AuthorityServiceUtils.NO_CHANGE, // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1236 AuthorityServiceUtils.NO_CHANGE); // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1237 } catch (Exception e) {
1238 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1241 return result.getBytes();
1244 public PoxPayloadOut updateAuthorityItem(
1245 ServiceContext itemServiceCtx, // Ok to be null. Will be null on PUT calls, but not on sync calls
1246 ResourceMap resourceMap,
1248 String parentspecifier,
1249 String itemspecifier,
1250 PoxPayloadIn theUpdate,
1251 boolean shouldUpdateRevNumber,
1254 ) throws Exception {
1255 PoxPayloadOut result = null;
1257 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
1258 String parentcsid = csidAndShortId.CSID;
1259 String parentShortId = csidAndShortId.shortIdentifier;
1261 // If the itemServiceCtx context is not null, use it. Otherwise, create a new context
1263 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
1265 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
1267 ctx.setInput(theUpdate); // the update payload
1270 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
1272 // We omit the parentShortId, only needed when doing a create...
1273 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
1274 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1275 if (isProposed != null) {
1276 handler.setIsProposed(isProposed);
1278 if (isSASItem != null) {
1279 handler.setIsSASItem(isSASItem);
1281 getRepositoryClient(ctx).update(ctx, itemcsid, handler);
1282 result = ctx.getOutput();
1288 * Delete authorityItem.
1290 * @param parentIdentifier the parentcsid
1291 * @param itemIdentifier the itemcsid
1293 * @return the response
1296 @Path("{csid}/items/{itemcsid}")
1297 public Response deleteAuthorityItem(
1298 @Context UriInfo uriInfo,
1299 @PathParam("csid") String parentIdentifier,
1300 @PathParam("itemcsid") String itemIdentifier) {
1301 Response result = null;
1303 ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1304 ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1305 if (logger.isDebugEnabled()) {
1306 logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1310 ServiceContext ctx = createServiceContext(getItemServiceName(), uriInfo);
1311 deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1312 result = Response.status(HttpResponseCodes.SC_OK).build();
1313 } catch (Exception e) {
1314 throw bigReThrow(e, ServiceMessages.DELETE_FAILED + " itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1322 * @param existingCtx
1323 * @param parentIdentifier
1324 * @param itemIdentifier
1327 @SuppressWarnings("rawtypes")
1328 public void deleteAuthorityItem(ServiceContext existingCtx,
1329 String parentIdentifier,
1330 String itemIdentifier,
1331 boolean shouldUpdateRevNumber
1332 ) throws Exception {
1333 Response result = null;
1335 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1336 if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1337 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1338 ctx.setProperties(existingCtx.getProperties());
1341 String parentcsid = null;
1343 parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1344 } catch (DocumentNotFoundException de) {
1345 logger.warn(String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1346 parentIdentifier, itemIdentifier));
1348 String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1350 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
1351 ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, shouldUpdateRevNumber); // Sometimes we can only soft-delete, so if it is during a sync we dont' update the revision number
1353 getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1357 @Path("{csid}/items/{itemcsid}/" + hierarchy)
1358 @Produces("application/xml")
1359 public String getHierarchy(
1360 @PathParam("csid") String parentIdentifier,
1361 @PathParam("itemcsid") String itemIdentifier,
1362 @Context UriInfo uriInfo) throws Exception {
1363 String result = null;
1367 // 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...?
1369 String calledUri = uriInfo.getPath();
1370 String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1371 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1373 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1374 String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1376 String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1377 if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1378 result = Hierarchy.surface(ctx, itemcsid, uri);
1380 result = Hierarchy.dive(ctx, itemcsid, uri);
1382 } catch (Exception e) {
1383 throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1394 protected String getItemDocType(String tenantId) {
1395 return getDocType(tenantId, getItemServiceName());
1399 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1400 * for the current resource, for all tenants
1402 * @return a map of URI templates for the current resource, for all tenants
1405 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1406 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1407 super.getUriRegistryEntries();
1408 List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1409 for (String tenantId : tenantIds) {
1410 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1412 return uriRegistryEntriesMap;