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.UriInfoWrapper;
57 import org.collectionspace.services.common.UriTemplateFactory;
58 import org.collectionspace.services.common.UriTemplateRegistry;
59 import org.collectionspace.services.common.UriTemplateRegistryKey;
60 import org.collectionspace.services.common.api.RefName;
61 import org.collectionspace.services.common.api.Tools;
62 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
63 import org.collectionspace.services.common.authorityref.AuthorityRefList;
64 import org.collectionspace.services.common.context.JaxRsContext;
65 import org.collectionspace.services.common.context.MultipartServiceContext;
66 import org.collectionspace.services.common.context.RemoteServiceContext;
67 import org.collectionspace.services.common.context.ServiceBindingUtils;
68 import org.collectionspace.services.common.context.ServiceContext;
69 import org.collectionspace.services.common.document.DocumentException;
70 import org.collectionspace.services.common.document.DocumentFilter;
71 import org.collectionspace.services.common.document.DocumentHandler;
72 import org.collectionspace.services.common.document.DocumentNotFoundException;
73 import org.collectionspace.services.common.document.DocumentReferenceException;
74 import org.collectionspace.services.common.document.DocumentWrapper;
75 import org.collectionspace.services.common.document.Hierarchy;
76 import org.collectionspace.services.common.query.QueryManager;
77 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler;
78 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler;
79 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
80 import org.collectionspace.services.config.ClientType;
81 import org.collectionspace.services.config.service.ServiceBindingType;
82 import org.collectionspace.services.jaxb.AbstractCommonList;
83 import org.collectionspace.services.lifecycle.TransitionDef;
84 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
85 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
86 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentFilter;
87 import org.collectionspace.services.nuxeo.client.java.RepositoryClientImpl;
88 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
89 import org.collectionspace.services.workflow.WorkflowCommon;
90 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
91 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
92 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
93 import org.collectionspace.services.description.ServiceDescription;
94 import org.jboss.resteasy.util.HttpResponseCodes;
95 import org.nuxeo.ecm.core.api.DocumentModel;
96 import org.nuxeo.ecm.core.api.DocumentModelList;
97 import org.slf4j.Logger;
98 import org.slf4j.LoggerFactory;
101 * The Class AuthorityResource.
104 @Consumes("application/xml")
105 @Produces("application/xml")
106 public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
107 extends NuxeoBasedResource {
109 final static String SEARCH_TYPE_TERMSTATUS = "ts";
110 public final static String hierarchy = "hierarchy";
112 protected Class<AuthCommon> authCommonClass;
113 protected Class<?> resourceClass;
114 protected String authorityCommonSchemaName;
115 protected String authorityItemCommonSchemaName;
116 final static ClientType CLIENT_TYPE = ServiceMain.getInstance().getClientType(); //FIXME: REM - 3 Why is this field needed? I see no references to it.
118 final static String FETCH_SHORT_ID = "_fetch_";
119 public final static String PARENT_WILDCARD = "_ALL_";
121 final Logger logger = LoggerFactory.getLogger(AuthorityResource.class);
124 * Instantiates a new Authority resource.
126 public AuthorityResource(Class<AuthCommon> authCommonClass, Class<?> resourceClass,
127 String authorityCommonSchemaName, String authorityItemCommonSchemaName) {
128 this.authCommonClass = authCommonClass;
129 this.resourceClass = resourceClass;
130 this.authorityCommonSchemaName = authorityCommonSchemaName;
131 this.authorityItemCommonSchemaName = authorityItemCommonSchemaName;
134 public abstract String getItemServiceName();
136 public abstract String getItemTermInfoGroupXPathBase();
139 protected String getVersionString() {
140 return "$LastChangedRevision: 2617 $";
144 public Class<AuthCommon> getCommonPartClass() {
145 return authCommonClass;
149 * Creates the item document handler.
152 * @param inAuthority the in vocabulary
154 * @return the document handler
156 * @throws Exception the exception
158 protected DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> createItemDocumentHandler(
159 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
160 String inAuthority, String parentShortIdentifier)
162 String authorityRefNameBase;
163 AuthorityItemDocumentModelHandler<?> docHandler;
165 if (parentShortIdentifier == null) {
166 authorityRefNameBase = null;
168 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getServiceName());
169 if (parentShortIdentifier.equals(FETCH_SHORT_ID)) { // We need to fetch this from the repo
170 if (ctx.getCurrentRepositorySession() != null) {
171 parentCtx.setCurrentRepositorySession(ctx.getCurrentRepositorySession()); // We need to use the current repo session if one exists
173 // Get from parent document
174 parentShortIdentifier = getAuthShortIdentifier(parentCtx, inAuthority);
176 authorityRefNameBase = buildAuthorityRefNameBase(parentCtx, parentShortIdentifier);
179 docHandler = (AuthorityItemDocumentModelHandler<?>) createDocumentHandler(ctx,
180 ctx.getCommonPartLabel(getItemServiceName()),
182 // FIXME - Richard and Aron think the following three lines should
183 // be in the constructor for the AuthorityItemDocumentModelHandler
184 // because all three are required fields.
185 docHandler.setInAuthority(inAuthority);
186 docHandler.setAuthorityRefNameBase(authorityRefNameBase);
187 docHandler.setItemTermInfoGroupXPathBase(getItemTermInfoGroupXPathBase());
191 public String getAuthShortIdentifier(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String authCSID)
192 throws DocumentNotFoundException, DocumentException {
193 String shortIdentifier = null;
196 AuthorityDocumentModelHandler<?> handler = (AuthorityDocumentModelHandler<?>) createDocumentHandler(ctx);
197 shortIdentifier = handler.getShortIdentifier(ctx, authCSID, authorityCommonSchemaName);
198 } catch (Exception e) {
199 if (logger.isDebugEnabled()) {
200 logger.debug("Caught exception ", e);
202 throw new DocumentException(e);
205 return shortIdentifier;
208 protected String buildAuthorityRefNameBase(
209 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String shortIdentifier) {
210 RefName.Authority authority = RefName.Authority.buildAuthority(ctx.getTenantName(),
211 ctx.getServiceName(),
212 null, // Only use shortId form!!!
213 shortIdentifier, null);
214 return authority.toString();
217 public static class CsidAndShortIdentifier {
219 String shortIdentifier;
222 protected String lookupParentCSID(String parentspecifier, String method,
223 String op, UriInfo uriInfo) throws Exception {
224 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(NULL_CONTEXT,
225 parentspecifier, method, op, uriInfo);
226 return tempResult.CSID;
229 protected String lookupParentCSID(ServiceContext ctx, String parentspecifier, String method,
230 String op, UriInfo uriInfo) throws Exception {
231 CsidAndShortIdentifier tempResult = lookupParentCSIDAndShortIdentifer(ctx,
232 parentspecifier, method, op, uriInfo);
233 return tempResult.CSID;
237 private CsidAndShortIdentifier lookupParentCSIDAndShortIdentifer(
238 ServiceContext existingCtx, // Ok to be null
239 String parentIdentifier,
244 CsidAndShortIdentifier result = new CsidAndShortIdentifier();
245 Specifier parentSpec = Specifier.getSpecifier(parentIdentifier, method, op);
248 String parentShortIdentifier;
249 if (parentSpec.form == SpecifierForm.CSID) {
250 parentShortIdentifier = null;
251 parentcsid = parentSpec.value;
252 // Uncomment when app layer is ready to integrate
253 // Uncommented since refNames are currently only generated if not present - ADR CSPACE-3178
254 parentShortIdentifier = FETCH_SHORT_ID;
256 parentShortIdentifier = parentSpec.value;
257 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, parentShortIdentifier);
258 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getServiceName(), uriInfo);
259 CoreSessionInterface repoSession = null;
260 if (existingCtx != null) {
261 repoSession = (CoreSessionInterface) existingCtx.getCurrentRepositorySession(); // We want to use the thread's current repo session
263 parentcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, whereClause); //FIXME: REM - If the parent has been soft-deleted, should we be looking for the item?
266 result.CSID = parentcsid;
267 result.shortIdentifier = parentShortIdentifier;
272 public String lookupItemCSID(ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext, String itemspecifier, String parentcsid, String method, String op)
276 Specifier itemSpec = Specifier.getSpecifier(itemspecifier, method, op);
277 if (itemSpec.form == SpecifierForm.CSID) {
278 itemcsid = itemSpec.value;
280 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
281 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(getItemServiceName());
282 CoreSessionInterface repoSession = null;
283 if (existingContext != null) {
284 repoSession = (CoreSessionInterface) existingContext.getCurrentRepositorySession(); // We want to use the thread's current repo session
286 itemcsid = getRepositoryClient(ctx).findDocCSID(repoSession, ctx, itemWhereClause); //FIXME: REM - Should we be looking for the 'wf_deleted' query param and filtering on it?
293 * Generally, callers will first call RefName.AuthorityItem.parse with a refName, and then
294 * use the returned item.inAuthority.resource and a resourceMap to get a service-specific
295 * Resource. They then call this method on that resource.
298 public DocumentModel getDocModelForAuthorityItem(CoreSessionInterface repoSession, RefName.AuthorityItem item)
299 throws Exception, DocumentNotFoundException {
303 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, item.getParentShortIdentifier());
304 // Ensure we have the right context.
305 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(item.inAuthority.resource);
307 // HACK - this really must be moved to the doc handler, not here. No Nuxeo specific stuff here!
308 RepositoryClientImpl client = (RepositoryClientImpl)getRepositoryClient(ctx);
309 String parentcsid = client.findDocCSID(repoSession, ctx, whereClause);
311 String itemWhereClause = RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, item.getShortIdentifier(), parentcsid);
312 ctx = createServiceContext(getItemServiceName());
313 DocumentWrapper<DocumentModel> docWrapper = client.findDoc(repoSession, ctx, itemWhereClause);
314 DocumentModel docModel = docWrapper.getWrappedObject();
320 public Response createAuthority(String xmlPayload) {
322 // 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
323 // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
324 // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
325 // the code that creates new authorities. The authority document model handler will first check for authorities with the same short id before
326 // trying to create a new authority.
328 synchronized(AuthorityResource.class) {
330 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
331 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
332 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
334 String csid = getRepositoryClient(ctx).create(ctx, handler);
335 UriBuilder path = UriBuilder.fromResource(resourceClass);
336 path.path("" + csid);
337 Response response = Response.created(path.build()).build();
339 } catch (Exception e) {
340 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
345 protected boolean supportsReplicating(String tenantId, String serviceName) {
346 boolean result = false;
348 ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, getServiceName());
349 result = sb.isSupportsReplicating();
355 * Synchronizes the authority and its items/terms with a Shared Authority Server.
357 * @param specifier either a CSID or one of the urn forms
359 * @return the authority
363 public byte[] synchronize(
364 @Context Request request,
365 @Context UriInfo uriInfo,
366 @PathParam("csid") String identifier) {
367 uriInfo = new UriInfoWrapper(uriInfo);
369 boolean neededSync = false;
370 PoxPayloadOut payloadOut = null;
374 // Prevent multiple SAS synchronizations from occurring simultaneously by synchronizing this method.
376 synchronized(AuthorityResource.class) {
378 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
380 * Make sure this authority service supports synchronization
382 if (supportsReplicating(ctx.getTenantId(), ctx.getServiceName()) == false) {
383 throw new DocumentException(Response.Status.FORBIDDEN.getStatusCode());
385 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
386 specifier = Specifier.getSpecifier(identifier, "getAuthority", "GET");
387 handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
388 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
389 payloadOut = ctx.getOutput();
390 } catch (Exception e) {
391 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, identifier);
395 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
397 if (neededSync == true) {
398 result = payloadOut.getBytes();
400 result = String.format("Authority resource '%s' was already in sync with shared authority server.",
401 specifier.value).getBytes();
402 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
403 throw new CSWebApplicationException(response);
411 * Gets the authority.
413 * @param specifier either a CSID or one of the urn forms
415 * @return the authority
421 @Context Request request,
422 @Context UriInfo uriInfo,
423 @PathParam("csid") String specifier) {
424 uriInfo = new UriInfoWrapper(uriInfo);
425 PoxPayloadOut result = null;
427 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
428 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
430 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
431 if (spec.form == SpecifierForm.CSID) {
432 if (logger.isDebugEnabled()) {
433 logger.debug("getAuthority with csid=" + spec.value);
435 getRepositoryClient(ctx).get(ctx, spec.value, handler);
437 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
438 DocumentFilter myFilter = new NuxeoDocumentFilter(whereClause, 0, 1);
439 handler.setDocumentFilter(myFilter);
440 getRepositoryClient(ctx).get(ctx, handler);
442 result = ctx.getOutput();
444 } catch (Exception e) {
445 throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
448 if (result == null) {
449 Response response = Response.status(Response.Status.NOT_FOUND).entity(
450 "Get failed, the requested Authority specifier:" + specifier + ": was not found.").type(
451 "text/plain").build();
452 throw new CSWebApplicationException(response);
455 return result.getBytes();
459 * Finds and populates the authority list.
463 * @return the authority list
466 @Produces("application/xml")
467 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.
468 uriInfo = new UriInfoWrapper(uriInfo);
469 AbstractCommonList result = null;
472 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
473 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
475 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
476 DocumentFilter myFilter = handler.getDocumentFilter();
477 // Need to make the default sort order for authority items
478 // be on the displayName field
479 String sortBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
480 if (sortBy == null || sortBy.isEmpty()) {
481 String qualifiedDisplayNameField = authorityCommonSchemaName + ":"
482 + AuthorityItemJAXBSchema.DISPLAY_NAME;
483 myFilter.setOrderByClause(qualifiedDisplayNameField);
485 String nameQ = queryParams.getFirst("refName");
487 myFilter.setWhereClause(authorityCommonSchemaName + ":refName='" + nameQ + "'");
489 getRepositoryClient(ctx).getFiltered(ctx, handler);
490 result = handler.getCommonPartList();
491 } catch (Exception e) {
492 throw bigReThrow(e, ServiceMessages.GET_FAILED);
499 * Overriding this methods to see if we should update the revision number during the update. We don't
500 * want to update the rev number of synchronization operations.
503 protected PoxPayloadOut update(String csid,
504 PoxPayloadIn theUpdate, // not used in this method, but could be used by an overriding method
505 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx)
507 AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler) createDocumentHandler(ctx);
508 Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY);
509 if (shouldUpdateRev != null) {
510 handler.setShouldUpdateRevNumber(shouldUpdateRev);
512 getRepositoryClient(ctx).update(ctx, csid, handler);
513 return ctx.getOutput();
519 * @param specifier the csid or id
521 * @return the multipart output
525 public byte[] updateAuthority(
526 @PathParam("csid") String specifier,
528 PoxPayloadOut result = null;
530 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
531 Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
532 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
533 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
535 if (spec.form == SpecifierForm.CSID) {
538 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
539 csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
541 getRepositoryClient(ctx).update(ctx, csid, handler);
542 result = ctx.getOutput();
543 } catch (Exception e) {
544 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
546 return result.getBytes();
552 * @param csid the csid
554 * @return the response
559 public Response old_deleteAuthority(@PathParam("csid") String csid) {
560 if (logger.isDebugEnabled()) {
561 logger.debug("deleteAuthority with csid=" + csid);
564 ensureCSID(csid, ServiceMessages.DELETE_FAILED, "Authority.csid");
565 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext();
566 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
567 getRepositoryClient(ctx).delete(ctx, csid, handler);
568 return Response.status(HttpResponseCodes.SC_OK).build();
569 } catch (Exception e) {
570 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, csid);
577 * @param csid the csid or a URN specifier form -e.g., urn:cspace:name(OurMuseumPersonAuthority)
579 * @return the response
583 public Response deleteAuthority(
584 @Context Request request,
585 @Context UriInfo uriInfo,
586 @PathParam("csid") String specifier) {
587 uriInfo = new UriInfoWrapper(uriInfo);
589 if (logger.isDebugEnabled()) {
590 logger.debug("deleteAuthority with specifier=" + specifier);
594 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(uriInfo);
595 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
597 Specifier spec = Specifier.getSpecifier(specifier, "getAuthority", "GET");
598 if (spec.form == SpecifierForm.CSID) {
599 if (logger.isDebugEnabled()) {
600 logger.debug("deleteAuthority with csid=" + spec.value);
602 ensureCSID(spec.value, ServiceMessages.DELETE_FAILED, "Authority.csid");
603 getRepositoryClient(ctx).delete(ctx, spec.value, handler);
605 if (logger.isDebugEnabled()) {
606 logger.debug("deleteAuthority with specifier=" + spec.value);
608 String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
609 getRepositoryClient(ctx).deleteWithWhereClause(ctx, whereClause, handler);
612 return Response.status(HttpResponseCodes.SC_OK).build();
613 } catch (Exception e) {
614 throw bigReThrow(e, ServiceMessages.DELETE_FAILED, specifier);
621 * @param parentspecifier - ID of the container. Can be URN or CSID form
622 * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
623 * @param isProposed - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
627 protected Response createAuthorityItem(ServiceContext ctx, String parentIdentifier,
628 boolean shouldUpdateRevNumber,
630 boolean isSasItem) throws Exception {
631 Response result = null;
633 // Note: must have the parentShortId, to do the create.
634 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "createAuthorityItem", "CREATE_ITEM", null);
635 AuthorityItemDocumentModelHandler handler =
636 (AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
637 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
638 handler.setIsProposed(isProposed);
639 handler.setIsSASItem(isSasItem);
640 // Make the client call
641 String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
643 // Build the JAX-RS response
644 UriBuilder path = UriBuilder.fromResource(resourceClass);
645 path.path(parent.CSID + "/items/" + itemcsid);
646 result = Response.created(path.build()).build();
652 * Called with an existing context.
654 * @param parentIdentifier
659 public Response createAuthorityItemWithParentContext(ServiceContext parentCtx,
660 String parentIdentifier,
662 boolean shouldUpdateRevNumber,
664 boolean isSASItem) throws Exception {
665 Response result = null;
667 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
668 parentCtx.getResourceMap(), parentCtx.getUriInfo());
669 if (parentCtx.getCurrentRepositorySession() != null) {
670 ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
672 result = this.createAuthorityItem(ctx, parentIdentifier, shouldUpdateRevNumber, isProposed, isSASItem);
677 /*************************************************************************
678 * Create an AuthorityItem - this is a sub-resource of Authority
679 * @param specifier either a CSID or one of the urn forms
680 * @return Authority item response
681 *************************************************************************/
683 @Path("{csid}/items")
684 public Response createAuthorityItem(
685 @Context ResourceMap resourceMap,
686 @Context UriInfo uriInfo,
687 @PathParam("csid") String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
689 uriInfo = new UriInfoWrapper(uriInfo);
690 Response result = null;
693 PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
694 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
695 result = this.createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
696 AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
697 } catch (Exception e) {
698 throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
705 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH)
706 public byte[] getItemWorkflow(
707 @PathParam("csid") String csid,
708 @PathParam("itemcsid") String itemcsid) {
709 PoxPayloadOut result = null;
712 ServiceContext<PoxPayloadIn, PoxPayloadOut> parentCtx = createServiceContext(getItemServiceName());
713 String parentWorkspaceName = parentCtx.getRepositoryWorkspaceName();
715 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME);
716 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
717 ctx.setRespositoryWorkspaceName(parentWorkspaceName); //find the document in the parent's workspace
718 getRepositoryClient(ctx).get(ctx, itemcsid, handler);
719 result = ctx.getOutput();
720 } catch (Exception e) {
721 throw bigReThrow(e, ServiceMessages.READ_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
723 return result.getBytes();
726 //FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
727 // they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
729 @Path("{csid}/items/{itemcsid}" + WorkflowClient.SERVICE_PATH + "/{transition}")
730 public byte[] updateItemWorkflowWithTransition(
731 @Context UriInfo uriInfo,
732 @PathParam("csid") String parentIdentifier,
733 @PathParam("itemcsid") String itemIdentifier,
734 @PathParam("transition") String transition) {
735 uriInfo = new UriInfoWrapper(uriInfo);
736 PoxPayloadOut result = null;
739 ServiceContext ctx = createServiceContext(getItemServiceName(), uriInfo);
740 result = updateItemWorkflowWithTransition(ctx,
741 parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.UPDATE_REV);
742 } catch (Exception e) {
743 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, parentIdentifier);
746 return result.getBytes();
750 * Update an authority item's workflow state.
751 * @param existingContext
756 * @throws DocumentReferenceException
758 public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext existingContext,
759 String parentIdentifier,
760 String itemIdentifier,
762 boolean updateRevNumber) throws DocumentReferenceException {
763 PoxPayloadOut result = null;
767 // We need CSIDs for both the parent authority and the authority item
769 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(existingContext, parentIdentifier, "updateItemWorkflowWithTransition(parent)", "UPDATE_ITEM", null);
770 String itemCsid = lookupItemCSID(existingContext, itemIdentifier, csidAndShortId.CSID, "updateAuthorityItem(item)", "UPDATE_ITEM");
773 // Create an empty workflow_commons input part and set it into a new "workflow" sub-resource context
775 PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(),
776 WorkflowClient.SERVICE_COMMONPART_NAME);
777 MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input);
778 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
779 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one
782 // Create a service context and document handler for the target resource -not the workflow resource itself.
784 ServiceContext<PoxPayloadIn, PoxPayloadOut> targetCtx = createServiceContext(getItemServiceName(), existingContext.getUriInfo());
785 AuthorityItemDocumentModelHandler targetDocHandler = (AuthorityItemDocumentModelHandler) this.createDocumentHandler(targetCtx);
786 targetDocHandler.setShouldUpdateRevNumber(updateRevNumber);
787 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
789 // When looking for the document, we need to use the parent/target resource's workspace name -not the "workflow" workspace name
791 String targetWorkspaceName = targetCtx.getRepositoryWorkspaceName();
792 ctx.setRespositoryWorkspaceName(targetWorkspaceName); //find the document in the parent's workspace
794 // Get the type of transition we're being asked to make and store it as a context parameter -used by the workflow document handler
795 TransitionDef transitionDef = getTransitionDef(targetCtx, transition);
796 if (transitionDef == null) {
797 throw new DocumentException(String.format("The document with ID='%s' does not support the workflow transition '%s'.",
798 itemIdentifier, transition));
800 ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
802 WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
803 getRepositoryClient(ctx).update(ctx, itemCsid, handler);
804 result = ctx.getOutput();
805 } catch (DocumentReferenceException de) {
807 } catch (Exception e) {
808 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemIdentifier);
814 private PoxPayloadOut getAuthorityItem(
816 String parentIdentifier,
817 String itemIdentifier) throws Exception {
818 PoxPayloadOut result = null;
820 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "getAuthorityItem(parent)", "GET_ITEM", null);
821 // We omit the parentShortId, only needed when doing a create...
822 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createItemDocumentHandler(ctx, parentcsid, null);
824 Specifier itemSpec = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem(item)", "GET_ITEM");
825 if (itemSpec.form == SpecifierForm.CSID) {
826 // TODO should we assert that the item is in the passed vocab?
827 getRepositoryClient(ctx).get(ctx, itemSpec.value, handler);
829 String itemWhereClause =
830 RefNameServiceUtils.buildWhereForAuthItemByName(authorityItemCommonSchemaName, itemSpec.value, parentcsid);
831 DocumentFilter myFilter = new NuxeoDocumentFilter(itemWhereClause, 0, 1); // start at page 0 and get 1 item
832 handler.setDocumentFilter(myFilter);
833 getRepositoryClient(ctx).get(ctx, handler);
836 result = (PoxPayloadOut) ctx.getOutput();
837 if (result != null) {
838 String inAuthority = XmlTools.getElementValue(result.getDOMDocument(), "//" + AuthorityItemJAXBSchema.IN_AUTHORITY);
839 if (inAuthority.equalsIgnoreCase(parentcsid) == false) {
840 throw new Exception(String.format("Looked up item = '%s' and found with inAuthority = '%s', but expected inAuthority = '%s'.",
841 itemSpec.value, inAuthority, parentcsid));
848 public PoxPayloadOut getAuthorityItemWithExistingContext(
849 ServiceContext existingCtx,
850 String parentIdentifier,
851 String itemIdentifier) throws Exception {
852 PoxPayloadOut result = null;
854 ServiceContext ctx = createServiceContext(getItemServiceName(), existingCtx.getResourceMap(), existingCtx.getUriInfo());
855 if (existingCtx.getCurrentRepositorySession() != null) {
856 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Reuse the current repo session if one exists
857 ctx.setProperties(existingCtx.getProperties());
859 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
865 * Gets the authority item.
867 * @param parentspecifier either a CSID or one of the urn forms
868 * @param itemspecifier either a CSID or one of the urn forms
870 * @return the authority item
873 @Path("{csid}/items/{itemcsid}")
874 public byte[] getAuthorityItem(
875 @Context Request request,
876 @Context UriInfo uriInfo,
877 @Context ResourceMap resourceMap,
878 @PathParam("csid") String parentIdentifier,
879 @PathParam("itemcsid") String itemIdentifier) {
880 uriInfo = new UriInfoWrapper(uriInfo);
881 PoxPayloadOut result = null;
883 RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx =
884 (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(getItemServiceName(), resourceMap, uriInfo);
886 JaxRsContext jaxRsContext = new JaxRsContext(request, uriInfo); // Needed for getting account permissions part of the resource
887 ctx.setJaxRsContext(jaxRsContext);
889 result = getAuthorityItem(ctx, parentIdentifier, itemIdentifier);
890 } catch (DocumentNotFoundException dnf) {
891 throw bigReThrow(dnf, ServiceMessages.resourceNotFoundMsg(itemIdentifier));
892 } catch (Exception e) {
893 throw bigReThrow(e, ServiceMessages.GET_FAILED);
896 return result.getBytes();
900 * Most of the authority child classes will/should use this implementation. However, the Vocabulary service's item schema is
901 * different enough that it will have to override this method in it's resource class.
904 protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
905 String result = null;
907 result = NuxeoUtils.getPrimaryElPathPropertyName(
908 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
909 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
915 protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
916 String result = null;
918 result = NuxeoUtils.getMultiElPathPropertyName(
919 authorityItemCommonSchemaName, getItemTermInfoGroupXPathBase(),
920 AuthorityItemJAXBSchema.TERM_DISPLAY_NAME);
926 * Gets the authorityItem list for the specified authority
927 * If partialPerm is specified, keywords will be ignored.
929 * @param authorityIdentifier either a CSID or one of the urn forms
930 * @param partialTerm if non-null, matches partial terms
931 * @param keywords if non-null, matches terms in the keyword index for items
932 * @param ui passed to include additional parameters, like pagination controls
935 public AbstractCommonList getAuthorityItemList(ServiceContext existingContext,
936 String authorityIdentifier,
937 UriInfo uriInfo) throws Exception {
938 AbstractCommonList result = null;
940 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
941 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
942 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
943 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
944 ctx.setProperties(existingContext.getProperties());
947 String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
948 String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
949 String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
950 String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
951 String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
953 // For the wildcard case, parentcsid is null, but docHandler will deal with this.
954 // We omit the parentShortId, only needed when doing a create...
955 String parentcsid = PARENT_WILDCARD.equals(authorityIdentifier) ? null :
956 lookupParentCSID(ctx, authorityIdentifier, "getAuthorityItemList", "LIST", uriInfo);
957 DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
958 createItemDocumentHandler(ctx, parentcsid, null);
960 DocumentFilter myFilter = handler.getDocumentFilter();
961 // If we are not wildcarding the parent, add a restriction
962 if (parentcsid != null) {
963 myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
964 + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
965 + "'" + parentcsid + "'",
966 IQueryManager.SEARCH_QUALIFIER_AND);
969 if (Tools.notBlank(termStatus)) {
970 // Start with the qualified termStatus field
971 String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
972 + AuthorityItemJAXBSchema.TERM_STATUS;
973 String[] filterTerms = termStatus.trim().split("\\|");
974 String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
975 myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
978 result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
984 * Gets the authorityItem list for the specified authority
985 * If partialPerm is specified, keywords will be ignored.
987 * @param authorityIdentifier either a CSID or one of the urn forms
988 * @param partialTerm if non-null, matches partial terms
989 * @param keywords if non-null, matches terms in the keyword index for items
990 * @param ui passed to include additional parameters, like pagination controls
992 * @return the authorityItem list
995 @Path("{csid}/items")
996 @Produces("application/xml")
997 public AbstractCommonList getAuthorityItemList(@PathParam("csid") String authorityIdentifier,
998 @Context UriInfo uriInfo) {
999 uriInfo = new UriInfoWrapper(uriInfo);
1000 AbstractCommonList result = null;
1003 result = getAuthorityItemList(NULL_CONTEXT, authorityIdentifier, uriInfo);
1004 } catch (Exception e) {
1005 throw bigReThrow(e, ServiceMessages.LIST_FAILED);
1012 * @return the name of the property used to specify references for items in this type of
1013 * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
1014 * Some types (like Vocabulary) use a separate property.
1016 protected String getRefPropName() {
1017 return ServiceBindingUtils.AUTH_REF_PROP;
1021 * Gets the entities referencing this Authority item instance. The service type
1022 * can be passed as a query param "type", and must match a configured type
1023 * for the service bindings. If not set, the type defaults to
1024 * ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
1026 * @param parentspecifier either a CSID or one of the urn forms
1027 * @param itemspecifier either a CSID or one of the urn forms
1030 * @return the info for the referencing objects
1033 @Path("{csid}/items/{itemcsid}/refObjs")
1034 @Produces("application/xml")
1035 public AuthorityRefDocList getReferencingObjects(
1036 @PathParam("csid") String parentSpecifier,
1037 @PathParam("itemcsid") String itemSpecifier,
1038 @Context UriTemplateRegistry uriTemplateRegistry,
1039 @Context UriInfo uriInfo) {
1040 uriInfo = new UriInfoWrapper(uriInfo);
1041 AuthorityRefDocList authRefDocList = null;
1043 authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriTemplateRegistry, uriInfo);
1044 } catch (Exception e) {
1045 throw bigReThrow(e, ServiceMessages.GET_FAILED);
1048 if (authRefDocList == null) {
1049 Response response = Response.status(Response.Status.NOT_FOUND).entity(
1050 "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
1051 "text/plain").build();
1052 throw new CSWebApplicationException(response);
1054 return authRefDocList;
1057 public AuthorityRefDocList getReferencingObjects(
1058 ServiceContext existingContext,
1059 String parentspecifier,
1060 String itemspecifier,
1061 UriTemplateRegistry uriTemplateRegistry,
1062 UriInfo uriInfo) throws Exception {
1063 uriInfo = new UriInfoWrapper(uriInfo);
1064 AuthorityRefDocList authRefDocList = null;
1066 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1067 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1069 // Merge parts of existing context with our new context
1071 if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
1072 ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If one exists, use the existing repo session
1073 ctx.setProperties(existingContext.getProperties());
1076 String parentcsid = lookupParentCSID(ctx, parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
1077 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
1079 // Remove the "type" property from the query params
1080 List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
1081 if (serviceTypes == null || serviceTypes.isEmpty()) {
1082 serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
1085 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
1086 authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
1088 return authRefDocList;
1092 * Gets the authority terms used in the indicated Authority item.
1094 * @param parentspecifier either a CSID or one of the urn forms
1095 * @param itemspecifier either a CSID or one of the urn forms
1096 * @param ui passed to include additional parameters, like pagination controls
1098 * @return the authority refs for the Authority item.
1101 @Path("{csid}/items/{itemcsid}/authorityrefs")
1102 @Produces("application/xml")
1103 public AuthorityRefList getAuthorityItemAuthorityRefs(
1104 @PathParam("csid") String parentspecifier,
1105 @PathParam("itemcsid") String itemspecifier,
1106 @Context UriInfo uriInfo) {
1107 uriInfo = new UriInfoWrapper(uriInfo);
1108 AuthorityRefList authRefList = null;
1111 // Note that we have to create the service context for the Items, not the main service
1112 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1113 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
1114 String parentcsid = lookupParentCSID(parentspecifier, "getAuthorityItemAuthRefs(parent)", "GET_ITEM_AUTH_REFS", uriInfo);
1115 // We omit the parentShortId, only needed when doing a create...
1116 DocumentModelHandler<?, AbstractCommonList> handler =
1117 (DocumentModelHandler<?, AbstractCommonList>)createItemDocumentHandler(ctx, parentcsid, null /*no parent short ID*/);
1119 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getAuthorityItemAuthRefs(item)", "GET_ITEM_AUTH_REFS");
1121 List<RefNameServiceUtils.AuthRefConfigInfo> authRefsInfo = RefNameServiceUtils.getConfiguredAuthorityRefs(ctx);
1122 authRefList = handler.getAuthorityRefs(itemcsid, authRefsInfo);
1123 } catch (Exception e) {
1124 throw bigReThrow(e, ServiceMessages.GET_FAILED + " parentspecifier: " + parentspecifier + " itemspecifier:" + itemspecifier);
1131 * Synchronizes a local authority item with a share authority server (SAS) item.
1133 * @param parentIdentifier
1134 * @param itemIdentifier
1138 @SuppressWarnings("unchecked")
1139 private PoxPayloadOut synchronizeItem(
1141 String parentIdentifier,
1142 String itemIdentifier,
1143 boolean syncHierarchicalRelationships) throws Exception {
1144 PoxPayloadOut result = null;
1145 AuthorityItemSpecifier specifier;
1146 boolean neededSync = false;
1148 CsidAndShortIdentifier parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
1149 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
1150 handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
1151 handler.setIsSASItem(AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this is now a SAS controlled item
1152 handler.setShouldSyncHierarchicalRelationships(syncHierarchicalRelationships);
1153 // Create an authority item specifier
1154 Specifier parentSpecifier = Specifier.getSpecifier(parent.CSID, "getAuthority", "GET");
1155 Specifier itemSpecifier = Specifier.getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
1156 specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
1158 neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
1159 if (neededSync == true) {
1160 result = (PoxPayloadOut) ctx.getOutput();
1167 * Using the parent and item ID, sync the local item with the SAS (shared authority server)
1168 * Used by the AuthorityItemDocumentModelHandler when synchronizing a list of remote authority items with a
1169 * local authority. The parent context was created for the authority (parent) because the sync started there.
1170 * @param existingCtx
1171 * @param parentIdentifier
1172 * @param itemIdentifier
1176 public PoxPayloadOut synchronizeItemWithExistingContext(
1177 ServiceContext existingCtx,
1178 String parentIdentifier,
1179 String itemIdentifier,
1180 boolean syncHierarchicalRelationships
1181 ) throws Exception {
1182 PoxPayloadOut result = null;
1184 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(),
1185 existingCtx.getResourceMap(),
1186 existingCtx.getUriInfo());
1187 if (existingCtx.getCurrentRepositorySession() != null) {
1188 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1191 result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships);
1197 * Synchronizes an authority item and with a Shared Authority Server (SAS) item.
1199 * @param specifier either CSIDs and/or one of the urn forms
1201 * @return the authority item if it was updated/synchronized with SAS item; otherwise empty
1204 @Path("{csid}/items/{itemcsid}/sync")
1205 public byte[] synchronizeItem(
1206 @Context ResourceMap resourceMap,
1207 @Context UriInfo uriInfo,
1208 @PathParam("csid") String parentIdentifier,
1209 @PathParam("itemcsid") String itemIdentifier) {
1210 uriInfo = new UriInfoWrapper(uriInfo);
1212 boolean neededSync = false;
1213 PoxPayloadOut payloadOut = null;
1216 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), null, resourceMap, uriInfo);
1217 payloadOut = this.synchronizeItem(ctx, parentIdentifier, itemIdentifier, true);
1218 if (payloadOut != null) {
1221 } catch (Exception e) {
1222 throw bigReThrow(e, ServiceMessages.SYNC_FAILED, itemIdentifier);
1226 // If a sync was needed and was successful, return a copy of the updated resource. Acts like an UPDATE.
1228 if (neededSync == true) {
1229 result = payloadOut.getBytes();
1231 result = String.format("Authority item resource '%s' was already in sync with shared authority server.",
1232 itemIdentifier).getBytes();
1233 Response response = Response.status(Response.Status.NOT_MODIFIED).entity(result).type("text/plain").build();
1234 throw new CSWebApplicationException(response);
1241 * Update authorityItem.
1243 * @param parentspecifier either a CSID or one of the urn forms
1244 * @param itemspecifier either a CSID or one of the urn forms
1246 * @return the multipart output
1249 @Path("{csid}/items/{itemcsid}")
1250 public byte[] updateAuthorityItem(
1251 @Context ResourceMap resourceMap,
1252 @Context UriInfo uriInfo,
1253 @PathParam("csid") String parentSpecifier,
1254 @PathParam("itemcsid") String itemSpecifier,
1255 String xmlPayload) {
1256 uriInfo = new UriInfoWrapper(uriInfo);
1257 PoxPayloadOut result = null;
1260 PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
1261 result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
1262 AuthorityServiceUtils.UPDATE_REV, // passing TRUE so rev num increases, passing
1263 AuthorityServiceUtils.NO_CHANGE, // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
1264 AuthorityServiceUtils.NO_CHANGE); // don't change the state of the "sas" field -we could be performing a sync or just a plain update
1265 } catch (Exception e) {
1266 throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
1269 return result.getBytes();
1272 public PoxPayloadOut updateAuthorityItem(
1273 ServiceContext itemServiceCtx, // Ok to be null. Will be null on PUT calls, but not on sync calls
1274 ResourceMap resourceMap,
1276 String parentspecifier,
1277 String itemspecifier,
1278 PoxPayloadIn theUpdate,
1279 boolean shouldUpdateRevNumber,
1282 ) throws Exception {
1283 PoxPayloadOut result = null;
1285 CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
1286 String parentcsid = csidAndShortId.CSID;
1287 String parentShortId = csidAndShortId.shortIdentifier;
1289 // If the itemServiceCtx context is not null, use it. Otherwise, create a new context
1291 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = itemServiceCtx;
1293 ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
1295 ctx.setInput(theUpdate); // the update payload
1298 String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "updateAuthorityItem(item)", "UPDATE_ITEM"); //use itemServiceCtx if it is not null
1300 // We omit the parentShortId, only needed when doing a create...
1301 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
1302 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1303 if (isProposed != null) {
1304 handler.setIsProposed(isProposed);
1306 if (isSASItem != null) {
1307 handler.setIsSASItem(isSASItem);
1309 getRepositoryClient(ctx).update(ctx, itemcsid, handler);
1310 result = ctx.getOutput();
1316 * Delete authorityItem.
1318 * @param parentIdentifier the parentcsid
1319 * @param itemIdentifier the itemcsid
1321 * @return the response
1324 @Path("{csid}/items/{itemcsid}")
1325 public Response deleteAuthorityItem(
1326 @Context UriInfo uriInfo,
1327 @PathParam("csid") String parentIdentifier,
1328 @PathParam("itemcsid") String itemIdentifier) {
1329 uriInfo = new UriInfoWrapper(uriInfo);
1330 Response result = null;
1332 ensureCSID(parentIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
1333 ensureCSID(itemIdentifier, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
1334 if (logger.isDebugEnabled()) {
1335 logger.debug("deleteAuthorityItem with parentcsid=" + parentIdentifier + " and itemcsid=" + itemIdentifier);
1339 ServiceContext ctx = createServiceContext(getItemServiceName(), uriInfo);
1340 deleteAuthorityItem(ctx, parentIdentifier, itemIdentifier, AuthorityServiceUtils.UPDATE_REV);
1341 result = Response.status(HttpResponseCodes.SC_OK).build();
1342 } catch (Exception e) {
1343 throw bigReThrow(e, ServiceMessages.DELETE_FAILED + " itemcsid: " + itemIdentifier + " parentcsid:" + parentIdentifier);
1351 * @param existingCtx
1352 * @param parentIdentifier
1353 * @param itemIdentifier
1356 @SuppressWarnings("rawtypes")
1357 public boolean deleteAuthorityItem(ServiceContext existingCtx,
1358 String parentIdentifier,
1359 String itemIdentifier,
1360 boolean shouldUpdateRevNumber
1361 ) throws Exception {
1362 boolean result = true;
1364 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo());
1365 if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
1366 ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession());
1367 ctx.setProperties(existingCtx.getProperties());
1370 String parentcsid = null;
1372 parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1373 } catch (DocumentNotFoundException de) {
1374 logger.warn(String.format("Could not find parent with ID='%s' when trying to delete item ID='%s'",
1375 parentIdentifier, itemIdentifier));
1377 String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1379 AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx);
1380 handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
1381 result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler);
1387 @Path("{csid}/items/{itemcsid}/" + hierarchy)
1388 @Produces("application/xml")
1389 public String getHierarchy(
1390 @PathParam("csid") String parentIdentifier,
1391 @PathParam("itemcsid") String itemIdentifier,
1392 @Context UriInfo uriInfo) throws Exception {
1393 uriInfo = new UriInfoWrapper(uriInfo);
1394 String result = null;
1398 // 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...?
1400 String calledUri = uriInfo.getPath();
1401 String uri = "/" + calledUri.substring(0, (calledUri.length() - ("/" + hierarchy).length()));
1402 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
1404 String parentcsid = lookupParentCSID(ctx, parentIdentifier, "deleteAuthorityItem(parent)", "DELETE_ITEM", null);
1405 String itemcsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null
1407 String direction = uriInfo.getQueryParameters().getFirst(Hierarchy.directionQP);
1408 if (Tools.notBlank(direction) && Hierarchy.direction_parents.equals(direction)) {
1409 result = Hierarchy.surface(ctx, itemcsid, uri);
1411 result = Hierarchy.dive(ctx, itemcsid, uri);
1413 } catch (Exception e) {
1414 throw bigReThrow(e, "Error showing hierarchy for authority item: ", itemIdentifier);
1425 protected String getItemDocType(String tenantId) {
1426 return getDocType(tenantId, getItemServiceName());
1430 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
1431 * for the current resource, for all tenants
1433 * @return a map of URI templates for the current resource, for all tenants
1436 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
1437 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
1438 super.getUriRegistryEntries();
1439 List<String> tenantIds = getTenantBindingsReader().getTenantIds();
1440 for (String tenantId : tenantIds) {
1441 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getItemDocType(tenantId), UriTemplateFactory.ITEM));
1443 return uriRegistryEntriesMap;
1449 public ServiceDescription getDescription(ServiceContext ctx) {
1450 ServiceDescription result = super.getDescription(ctx);
1451 result.setSubresourceDocumentType(this.getItemDocType(ctx.getTenantId()));