final static String SHORT_IDENTIFIER = "shortIdentifier";
final static String CSID = "csid";
final static String REV = "rev";
+ final static String SAS = "sas";
+ final static String DEPRECATED = "deprecated";
+ final static String PROPOSED = "proposed";
final static String DISPLAY_NAME = "displayName"; // This is the display name element for the Vocabulary service's item
final static String TERM_DISPLAY_NAME = "termDisplayName"; // This is the display name element for all Authority services' items
final static String TERM_NAME = "termName";
final static String REF_NAME = "refName";
final static String SHORT_IDENTIFIER = "shortIdentifier";
final static String CSID = "csid";
- final static String REV = "rev";
+ final static String REV = "rev";
+ final static String SAS = "sas";
+ final static String PROPOSED = "proposed";
+ final static String DEPRECATED = "deprecated";
final static String URI = "url";
}
final static String VOCAB_TYPE = "vocabType";
final static String CSID = "csid";
final static String REV = "rev";
+ final static String SAS = "sas"; // boolean flag indicating if authority is a shared authority
}
final static String VOCAB_TYPE = "vocabType";
final static String CSID = "csid";
final static String REV = "rev";
+ final static String SAS = "sas"; // boolean flag indicating if authority is a shared authority item
final static String URI = "url";
}
/**
* The Class AuthorityResource.
*/
-/**
- * @author pschmitz
- *
- * @param <AuthCommon>
- * @param <AuthItemHandler>
- */
-/**
- * @author pschmitz
- *
- * @param <AuthCommon>
- * @param <AuthItemHandler>
- */
+
@Consumes("application/xml")
@Produces("application/xml")
public abstract class AuthorityResource<AuthCommon, AuthItemHandler>
extends NuxeoBasedResource {
final static String SEARCH_TYPE_TERMSTATUS = "ts";
+ public final static String hierarchy = "hierarchy";
protected Class<AuthCommon> authCommonClass;
protected Class<?> resourceClass;
ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(ui);
AuthorityDocumentModelHandler handler = (AuthorityDocumentModelHandler)createDocumentHandler(ctx);
specifier = getSpecifier(csid, "getAuthority", "GET");
+ handler.setShouldUpdateRevNumber(AuthorityServiceUtils.DONT_UPDATE_REV); // Never update rev number on sync calls
neededSync = getRepositoryClient(ctx).synchronize(ctx, specifier, handler);
payloadOut = ctx.getOutput();
} catch (Exception e) {
}
}
- protected Response createAuthorityItem(ServiceContext ctx, String parentspecifier, boolean shouldUpdateRevNumber) throws Exception {
+ /**
+ *
+ * @param ctx
+ * @param parentspecifier - ID of the container. Can be URN or CSID form
+ * @param shouldUpdateRevNumber - Indicates if the revision number should be updated on create -won't do this when synching with SAS
+ * @param proposed - In a shared authority context, indicates if this item just a proposed item and not yet part of the SAS authority
+ * @return
+ * @throws Exception
+ */
+ protected Response createAuthorityItem(ServiceContext ctx, String parentspecifier,
+ boolean shouldUpdateRevNumber,
+ boolean proposed) throws Exception {
Response result = null;
// Note: must have the parentShortId, to do the create.
AuthorityItemDocumentModelHandler handler =
(AuthorityItemDocumentModelHandler) createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
+ handler.setIsProposed(proposed);
String itemcsid = getRepositoryClient(ctx).create(ctx, handler);
UriBuilder path = UriBuilder.fromResource(resourceClass);
path.path(parent.CSID + "/items/" + itemcsid);
public Response createAuthorityItemWithParentContext(ServiceContext parentCtx,
String parentspecifier,
PoxPayloadIn input,
- boolean shouldUpdateRevNumber) throws Exception {
+ boolean shouldUpdateRevNumber,
+ boolean isProposed) throws Exception {
Response result = null;
ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input,
if (parentCtx.getCurrentRepositorySession() != null) {
ctx.setCurrentRepositorySession(parentCtx.getCurrentRepositorySession());
}
- result = this.createAuthorityItem(ctx, parentspecifier, shouldUpdateRevNumber);
+ result = this.createAuthorityItem(ctx, parentspecifier, shouldUpdateRevNumber, isProposed);
return result;
}
try {
PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
- result = this.createAuthorityItem(ctx, parentspecifier, AuthorityServiceUtils.UPDATE_REV);
+ result = this.createAuthorityItem(ctx, parentspecifier, AuthorityServiceUtils.UPDATE_REV,
+ AuthorityServiceUtils.PROPOSED);
} catch (Exception e) {
throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
}
/**
* Update an authority item's workflow state.
* @param existingContext
- * @param csid
- * @param itemcsid
+ * @param parentCsid
+ * @param itemCsid
* @param transition
* @return
*/
public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext existingContext,
- String csid,
- String itemcsid,
+ String parentCsid,
+ String itemCsid,
String transition,
boolean updateRevNumber) {
PoxPayloadOut result = null;
ctx.setProperty(WorkflowClient.TRANSITION_ID, transitionDef);
WorkflowDocumentModelHandler handler = createWorkflowDocumentHandler(ctx);
- getRepositoryClient(ctx).update(ctx, itemcsid, handler);
+ getRepositoryClient(ctx).update(ctx, itemCsid, handler);
result = ctx.getOutput();
} catch (Exception e) {
- throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
+ throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, itemCsid);
}
return result;
return result;
}
+ /**
+ * Return a paged list of authority items.
+ *
+ * @param existingContext
+ * @param specifier
+ * @param uriInfo
+ * @return
+ * @throws Exception
+ */
+ public AbstractCommonList getAuthorityItemList(ServiceContext existingContext,
+ String specifier,
+ UriInfo uriInfo) throws Exception {
+ AbstractCommonList result = null;
+
+ ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
+ MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
+ if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { // Merge some of the existing context properties with our new context
+ ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());
+ ctx.setProperties(existingContext.getProperties());
+ }
+
+ String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
+ String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
+ String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
+ String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
+ String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
+
+ // For the wildcard case, parentcsid is null, but docHandler will deal with this.
+ // We omit the parentShortId, only needed when doing a create...
+ String parentcsid = PARENT_WILDCARD.equals(specifier) ? null :
+ lookupParentCSID(specifier, "getAuthorityItemList", "LIST", uriInfo);
+ DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
+ createItemDocumentHandler(ctx, parentcsid, null);
+
+ DocumentFilter myFilter = handler.getDocumentFilter();
+ // If we are not wildcarding the parent, add a restriction
+ if (parentcsid != null) {
+ myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
+ + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
+ + "'" + parentcsid + "'",
+ IQueryManager.SEARCH_QUALIFIER_AND);
+ }
+
+ if (Tools.notBlank(termStatus)) {
+ // Start with the qualified termStatus field
+ String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
+ + AuthorityItemJAXBSchema.TERM_STATUS;
+ String[] filterTerms = termStatus.trim().split("\\|");
+ String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
+ myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
+ }
+
+ result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
+
+ return result;
+ }
+
/**
* Gets the authorityItem list for the specified authority
* If partialPerm is specified, keywords will be ignored.
AbstractCommonList result = null;
try {
- ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
- MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
-
- String orderBy = queryParams.getFirst(IClientQueryParams.ORDER_BY_PARAM);
- String termStatus = queryParams.getFirst(SEARCH_TYPE_TERMSTATUS);
- String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);
- String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);
- String partialTerm = queryParams.getFirst(IQueryManager.SEARCH_TYPE_PARTIALTERM);
-
- // For the wildcard case, parentcsid is null, but docHandler will deal with this.
- // We omit the parentShortId, only needed when doing a create...
- String parentcsid = PARENT_WILDCARD.equals(specifier) ? null :
- lookupParentCSID(specifier, "getAuthorityItemList", "LIST", uriInfo);
- DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler =
- createItemDocumentHandler(ctx, parentcsid, null);
-
- DocumentFilter myFilter = handler.getDocumentFilter();
- // If we are not wildcarding the parent, add a restriction
- if (parentcsid != null) {
- myFilter.appendWhereClause(authorityItemCommonSchemaName + ":"
- + AuthorityItemJAXBSchema.IN_AUTHORITY + "="
- + "'" + parentcsid + "'",
- IQueryManager.SEARCH_QUALIFIER_AND);
- }
-
- if (Tools.notBlank(termStatus)) {
- // Start with the qualified termStatus field
- String qualifiedTermStatusField = authorityItemCommonSchemaName + ":"
- + AuthorityItemJAXBSchema.TERM_STATUS;
- String[] filterTerms = termStatus.trim().split("\\|");
- String tsClause = QueryManager.createWhereClauseToFilterFromStringList(qualifiedTermStatusField, filterTerms, IQueryManager.FILTER_EXCLUDE);
- myFilter.appendWhereClause(tsClause, IQueryManager.SEARCH_QUALIFIER_AND);
- }
-
- result = search(ctx, handler, uriInfo, orderBy, keywords, advancedSearch, partialTerm);
+ result = getAuthorityItemList(NULL_CONTEXT, specifier, uriInfo);
} catch (Exception e) {
throw bigReThrow(e, ServiceMessages.LIST_FAILED);
}
* for the service bindings. If not set, the type defaults to
* ServiceBindingUtils.SERVICE_TYPE_PROCEDURE.
*
- * @param parentspecifier either a CSID or one of the urn forms
- * @param itemspecifier either a CSID or one of the urn forms
+ * @param parentSpecifier either a CSID or one of the urn forms
+ * @param itemSpecifier either a CSID or one of the urn forms
* @param ui the ui
*
* @return the info for the referencing objects
@Path("{csid}/items/{itemcsid}/refObjs")
@Produces("application/xml")
public AuthorityRefDocList getReferencingObjects(
- @PathParam("csid") String parentspecifier,
- @PathParam("itemcsid") String itemspecifier,
+ @PathParam("csid") String parentSpecifier,
+ @PathParam("itemcsid") String itemSpecifier,
@Context UriTemplateRegistry uriTemplateRegistry,
@Context UriInfo uriInfo) {
AuthorityRefDocList authRefDocList = null;
try {
- ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
- MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
-
- String parentcsid = lookupParentCSID(parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
- String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
-
- List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
- if(serviceTypes == null || serviceTypes.isEmpty()) {
- serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
- }
-
- // Note that we have to create the service context for the Items, not the main service
- // We omit the parentShortId, only needed when doing a create...
- AuthorityItemDocumentModelHandler<?> handler = (AuthorityItemDocumentModelHandler<?>)
- createItemDocumentHandler(ctx, parentcsid, null);
-
- authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
+ authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriTemplateRegistry, uriInfo);
} catch (Exception e) {
throw bigReThrow(e, ServiceMessages.GET_FAILED);
}
+
if (authRefDocList == null) {
Response response = Response.status(Response.Status.NOT_FOUND).entity(
- "Get failed, the requested Item CSID:" + itemspecifier + ": was not found.").type(
+ "Get failed, the requested Item CSID:" + itemSpecifier + ": was not found.").type(
"text/plain").build();
throw new CSWebApplicationException(response);
}
return authRefDocList;
}
+
+ public AuthorityRefDocList getReferencingObjects(
+ ServiceContext existingContext,
+ String parentspecifier,
+ String itemspecifier,
+ UriTemplateRegistry uriTemplateRegistry,
+ UriInfo uriInfo) throws Exception {
+ AuthorityRefDocList authRefDocList = null;
+
+ ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), uriInfo);
+ MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
+ //
+ // Merge parts of existing context with our new context
+ //
+ if (existingContext != null && existingContext.getCurrentRepositorySession() != null) {
+ ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession()); // If one exists, use the existing repo session
+ ctx.setProperties(existingContext.getProperties());
+ }
+
+ String parentcsid = lookupParentCSID(parentspecifier, "getReferencingObjects(parent)", "GET_ITEM_REF_OBJS", uriInfo);
+ String itemcsid = lookupItemCSID(ctx, itemspecifier, parentcsid, "getReferencingObjects(item)", "GET_ITEM_REF_OBJS");
+
+ List<String> serviceTypes = queryParams.remove(ServiceBindingUtils.SERVICE_TYPE_PROP);
+ if(serviceTypes == null || serviceTypes.isEmpty()) {
+ serviceTypes = ServiceBindingUtils.getCommonServiceTypes(true); //CSPACE-5359: Should now include objects, procedures, and authorities
+ }
+
+ // Note that we have to create the service handler for the Items, not the main parent resource
+ // We omit the parentShortId, only needed when doing a create...
+ AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
+ authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
+
+ return authRefDocList;
+ }
/**
* Gets the authority terms used in the indicated Authority item.
* @return
* @throws Exception
*/
- protected PoxPayloadOut synchronizeItem(
+ private PoxPayloadOut synchronizeItem(
ServiceContext ctx,
String parentIdentifier,
String itemIdentifier) throws Exception {
parent = lookupParentCSIDAndShortIdentifer(ctx, parentIdentifier, "syncAuthorityItem(parent)", "SYNC_ITEM", null);
AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parent.CSID, parent.shortIdentifier);
+ handler.setIsProposed(AuthorityServiceUtils.NOT_PROPOSED); // In case it was formally locally proposed, clear the proposed flag
+ // Create an authority item specifier
Specifier parentSpecifier = getSpecifier(parent.CSID, "getAuthority", "GET");
Specifier itemSpecifier = getSpecifier(itemIdentifier, "getAuthorityItem", "GET");
specifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier);
try {
PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
result = updateAuthorityItem(null, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
- AuthorityServiceUtils.UPDATE_REV); // passing TRUE so rev num increases
+ AuthorityServiceUtils.UPDATE_REV, // passing TRUE so rev num increases, passing
+ AuthorityServiceUtils.NO_CHANGE); // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
} catch (Exception e) {
throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
}
String parentspecifier,
String itemspecifier,
PoxPayloadIn theUpdate,
- boolean shouldUpdateRevNumber) throws Exception {
+ boolean shouldUpdateRevNumber,
+ Boolean isProposed) throws Exception {
PoxPayloadOut result = null;
CsidAndShortIdentifier csidAndShortId = lookupParentCSIDAndShortIdentifer(itemServiceCtx, parentspecifier, "updateAuthorityItem(parent)", "UPDATE_ITEM", null);
// We omit the parentShortId, only needed when doing a create...
AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, parentShortId);
handler.setShouldUpdateRevNumber(shouldUpdateRevNumber);
+ if (isProposed != null) {
+ handler.setIsProposed(isProposed);
+ }
getRepositoryClient(ctx).update(ctx, itemcsid, handler);
result = ctx.getOutput();
public Response deleteAuthorityItem(
@PathParam("csid") String parentcsid,
@PathParam("itemcsid") String itemcsid) {
- //try{
+ Response result = null;
+
if (logger.isDebugEnabled()) {
logger.debug("deleteAuthorityItem with parentcsid=" + parentcsid + " and itemcsid=" + itemcsid);
}
+
try {
- ensureCSID(parentcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
- ensureCSID(itemcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
- //Laramie, removing this catch, since it will surely fail below, since itemcsid or parentcsid will be null.
- // }catch (Throwable t){
- // System.out.println("ERROR in setting up DELETE: "+t);
- // }
- // try {
- // Note that we have to create the service context for the Items, not the main service
- ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName());
- DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
- getRepositoryClient(ctx).delete(ctx, itemcsid, handler);
- return Response.status(HttpResponseCodes.SC_OK).build();
+ deleteAuthorityItem(null, parentcsid, itemcsid);
+ result = Response.status(HttpResponseCodes.SC_OK).build();
} catch (Exception e) {
throw bigReThrow(e, ServiceMessages.DELETE_FAILED + " itemcsid: " + itemcsid + " parentcsid:" + parentcsid);
}
+
+ return result;
}
- public final static String hierarchy = "hierarchy";
-
+
+ public void deleteAuthorityItem(ServiceContext existingCtx,
+ String parentcsid,
+ String itemcsid) throws Exception {
+ Response result = null;
+
+ ensureCSID(parentcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.parentcsid");
+ ensureCSID(itemcsid, ServiceMessages.DELETE_FAILED, "AuthorityItem.itemcsid");
+ // Note that we have to create the service context for the Items, not the main service
+ ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName());
+ if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) {
+ ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); // Use existing repo session if one exists
+ ctx.setProperties(existingCtx.getProperties());
+ }
+ DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
+ getRepositoryClient(ctx).delete(ctx, itemcsid, handler);
+ }
+
@GET
@Path("{csid}/items/{itemcsid}/" + hierarchy)
@Produces("application/xml")
public class AuthorityServiceUtils {
private static final Logger logger = LoggerFactory.getLogger(AuthorityIdentifierUtils.class);
+ // Used to keep track if an authority item's is deprecated
+ public static final String IS_DEPRECATED_PROPERTY = "IS_DEPRECATED_PROPERTY";
+ public static final Boolean DEPRECATED = true;
+ public static final Boolean NOT_DEPRECATED = !DEPRECATED;
+
+ // Used to keep track if an authority item's rev number should be updated
public static final String SHOULD_UPDATE_REV_PROPERTY = "SHOULD_UPDATE_REV_PROPERTY";
- public static final boolean DONT_UPDATE_REV = false;
public static final boolean UPDATE_REV = true;
+ public static final boolean DONT_UPDATE_REV = !UPDATE_REV;
+
+ // Used to keep track if an authority item is a locally proposed member of a SAS authority
+ public static final String IS_PROPOSED_PROPERTY = "IS_PROPOSED";
+ public static final Boolean PROPOSED = true;
+ public static final Boolean NOT_PROPOSED = !PROPOSED;
+ public static final Boolean NO_CHANGE = null;
static public PoxPayloadIn requestPayloadIn(ServiceContext ctx, Specifier specifier, Class responseType) throws Exception {
PoxPayloadIn result = null;
*/
package org.collectionspace.services.common.vocabulary.nuxeo;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.ws.rs.core.Response;
+import org.collectionspace.services.client.AbstractCommonListUtils;
import org.collectionspace.services.client.AuthorityClient;
import org.collectionspace.services.client.CollectionSpaceClient;
import org.collectionspace.services.client.PayloadInputPart;
import org.collectionspace.services.client.VocabularyClient;
import org.collectionspace.services.client.PoxPayloadIn;
import org.collectionspace.services.client.PoxPayloadOut;
+import org.collectionspace.services.client.workflow.WorkflowClient;
import org.collectionspace.services.common.ResourceMap;
import org.collectionspace.services.common.XmlTools;
import org.collectionspace.services.common.api.RefName;
import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
import org.collectionspace.services.config.service.ObjectPartType;
+import org.collectionspace.services.jaxb.AbstractCommonList;
+import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
import org.collectionspace.services.lifecycle.TransitionDef;
import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler;
import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
Specifier specifier = (Specifier) wrapDoc.getWrappedObject();
//
- // Get the rev number of the authority so we can compare with rev number of shared authority
+ // Get the rev number and short ID of the local authority
//
DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, specifier);
Long localRev = (Long) NuxeoUtils.getProperyValue(docModel, AuthorityJAXBSchema.REV);
Specifier sasSpecifier = new Specifier(SpecifierForm.URN_NAME, RefNameUtils.createShortIdRefName(shortId));
PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadIn(ctx, sasSpecifier, getEntityResponseType());
//
+ // Compare revision number of local authority with that of the shared authority on the SAS.
// If the authority on the SAS is newer, synch all the items and then the authority record as well
//
Long sasRev = getRevision(sasPayloadIn);
if (sasRev > localRev) {
//
- // First, sync all the authority items
+ // First, synchronize all the authority item records/resources.
//
syncAllItems(ctx, sasSpecifier);
//
ResourceMap resourceMap = ctx.getResourceMap();
String resourceName = ctx.getClient().getServiceName();
AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
+ // Set this context property since we don't want to update the revision number on sync updates
+ ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, AuthorityServiceUtils.DONT_UPDATE_REV);
PoxPayloadOut payloadOut = authorityResource.update(ctx, resourceMap, ctx.getUriInfo(), docModel.getName(),
sasPayloadIn);
if (payloadOut != null) {
int created = 0;
int synched = 0;
int alreadySynched = 0;
+ int deprecated = 0;
int totalItemsProcessed = 0;
+ ArrayList<String> itemsInRemoteAuthority = new ArrayList<String>();
//
// Iterate over the list of items/terms in the remote authority
//
if (itemList != null) {
for (Element e:itemList) {
String remoteRefName = XmlTools.getElementValue(e, "refName");
+ itemsInRemoteAuthority.add(remoteRefName);
long status = syncRemoteItemWithLocalItem(ctx, remoteRefName);
if (status == 1) {
created++;
totalItemsProcessed++;
}
}
+ //
+ // Now see if we need to deprecate or delete items that have been hard-deleted from the SAS but still exist
+ // locally. Subtract (remove) the list of remote items from the list of local items to determine which
+ // of the remote items have been hard deleted.
+ //
+ ArrayList<String> itemsInLocalAuthority = getItemsInLocalAuthority(ctx, sasSpecifier);
+ if (itemsInLocalAuthority.removeAll(itemsInRemoteAuthority) == true) {
+ ArrayList<String> remainingItems = itemsInLocalAuthority; // now a subset of local items
+ //
+ // We now need to either hard-deleted or deprecate the remaining authorities
+ //
+ deleteOrDeprecateItems(ctx, remainingItems);
+ }
+
logger.info(String.format("Total number of items processed during sync: %d", totalItemsProcessed));
logger.info(String.format("Number of items synchronized: %d", synched));
return result;
}
+ private long deleteOrDeprecateItems(ServiceContext ctx, ArrayList<String> refNameList) throws Exception {
+ long result = 0;
+ ArrayList<String> failureList = new ArrayList<String>();
+
+ for (String refName:refNameList) {
+ AuthorityTermInfo itemInfo = RefNameUtils.parseAuthorityTermInfo(refName);
+ AuthorityResource authorityResource = (AuthorityResource) ctx.getResource();
+ try {
+ authorityResource.deleteAuthorityItem(ctx, itemInfo.inAuthority.csid, itemInfo.csid);
+ result++;
+ } catch (DocumentException de) {
+ //
+ // If we can't delete the item, it might be because there are existing records referencing it. So
+ // we need to mark the item as deprecated
+ //
+ logger.info(String.format("Hit document exception trying to delete or deprecate '%s' during sync",
+ refName), de);
+ boolean marked = markAuthorityItemAsDeprecated(ctx, itemInfo);
+ if (marked == true) {
+ result++;
+ }
+ } catch (Exception e) {
+ logger.warn(String.format("Unable to delete authority item '%s'", refName), e);
+ }
+ }
+
+ if (logger.isWarnEnabled() == true) {
+ if (result != refNameList.size()) {
+ logger.warn(String.format("Unable to delete or deprecate some authority items during synchronization with SAS. Deleted or deprecated %d of %d",
+ result, refNameList.size()));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Mark the authority item as deprecated.
+ *
+ * @param ctx
+ * @param itemInfo
+ * @throws Exception
+ */
+ private boolean markAuthorityItemAsDeprecated(ServiceContext ctx, AuthorityTermInfo itemInfo) throws Exception {
+ boolean result = false;
+
+ try {
+ String itemCsid = itemInfo.csid;
+ DocumentModel docModel = NuxeoUtils.getDocFromCsid(ctx, (CoreSessionInterface)ctx.getCurrentRepositorySession(), itemCsid);
+ docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.DEPRECATED,
+ new Boolean(AuthorityServiceUtils.DEPRECATED));
+ result = true;
+ } catch (Exception e) {
+ logger.warn(String.format("Could not mark item '%s' as deprecated.", itemInfo.name), e);
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the list of SAS related items in the local authority. We exlude items with the "proposed" flags because
+ * we want a list with only SAS created items.
+ *
+ * We need to add pagination support to this call!!!
+ *
+ * @param ctx
+ * @param specifier
+ * @return
+ * @throws Exception
+ */
+ private ArrayList<String> getItemsInLocalAuthority(ServiceContext ctx, Specifier specifier) throws Exception {
+ ArrayList<String> result = new ArrayList<String>();
+
+ ResourceMap resourceMap = ctx.getResourceMap();
+ String resourceName = ctx.getClient().getServiceName();
+ AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
+ AbstractCommonList acl = authorityResource.getAuthorityItemList(ctx, specifier.value, ctx.getUriInfo());
+ List<ListItem> listItemList = acl.getListItem();
+ for (ListItem listItem:listItemList) {
+ Boolean proposed = getBooleanValue(listItem, AuthorityItemJAXBSchema.PROPOSED);
+ if (proposed == false) { // exclude "proposed" (i.e., local-only items)
+ result.add(AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME));
+ }
+ }
+
+ return result;
+ }
+
+ private Boolean getBooleanValue(ListItem listItem, String name) {
+ Boolean result = null;
+
+ String value = AbstractCommonListUtils.ListItemGetElementValue(listItem, name);
+ if (value != null) {
+ result = Boolean.valueOf(value);
+ }
+
+ return result;
+ }
+
+ private String getStringValue(ListItem listItem, String name) {
+ return AbstractCommonListUtils.ListItemGetElementValue(listItem, AuthorityItemJAXBSchema.REF_NAME);
+ }
+
/**
* This is a sync method.
* @param ctx
String resourceName = ctx.getClient().getServiceName();
AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
Response response = authorityResource.createAuthorityItemWithParentContext(ctx, authoritySpecifier.value,
- sasPayloadIn, AuthorityServiceUtils.DONT_UPDATE_REV);
+ sasPayloadIn, AuthorityServiceUtils.DONT_UPDATE_REV, AuthorityServiceUtils.NOT_PROPOSED);
//
// Check the response for successful POST result
//
if (response.getStatus() != Response.Status.CREATED.getStatusCode()) {
throw new DocumentException(String.format("Could not create new authority item '%s' during synchronization of the '%s' authority.",
itemIdentifier, parentIdentifier));
+ }
//
- // Handle the workflow state
+ // Handle the workflow state by "locking" this new copy of the SAS item.
//
- }
+ authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier,
+ WorkflowClient.WORKFLOWTRANSITION_LOCK, AuthorityServiceUtils.DONT_UPDATE_REV);
}
/**
String parentIdentifier = RefNameUtils.createShortIdRefName(authorityTermInfo.inAuthority.name);
String itemIdentifier = RefNameUtils.createShortIdRefName(authorityTermInfo.name);
//
- // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates)
+ // We'll use the Authority JAX-RS resource class to peform sync operations (creates and updates)
//
ResourceMap resourceMap = ctx.getResourceMap();
String resourceName = ctx.getClient().getServiceName();
localItemPayloadOut = authorityResource.getAuthorityItemWithParentContext(ctx, parentIdentifier, itemIdentifier);
} catch (DocumentNotFoundException dnf) {
//
- // Document not found, means we need to create an item/term that exists only on the SAS
+ // Document not found, means we need to create a local item/term that exists only on the SAS
//
logger.info(String.format("Remote item with refname='%s' doesn't exist locally, so we'll create it.", remoteRefName));
createLocalItem(ctx, parentIdentifier, itemIdentifier);
return 1; // exit with status of 1 means we created a new authority item
}
//
- // If we get here, we know the item exists both locally and remotely, so we need to synchronize them
+ // If we get here, we know the item exists both locally and remotely, so we need to synchronize them.
+ // We synchronize by calling the authority resource (the JAX-RS resource) class' sync method for an item.
//
PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithParentContext(ctx, parentIdentifier, itemIdentifier);
if (theUpdate != null) {
import org.collectionspace.services.client.IQueryManager;
import org.collectionspace.services.client.PoxPayloadIn;
import org.collectionspace.services.client.PoxPayloadOut;
+import org.collectionspace.services.client.workflow.WorkflowClient;
import org.collectionspace.services.common.ResourceMap;
+import org.collectionspace.services.common.ServiceMain;
import org.collectionspace.services.common.UriTemplateRegistry;
import org.collectionspace.services.common.api.RefName;
import org.collectionspace.services.common.api.RefNameUtils;
protected String authorityCommonSchemaName;
protected String authorityItemCommonSchemaName;
private String authorityItemTermGroupXPathBase;
+
+ private boolean isProposed = false; // used by local authority to propose a new shared item. Allows local deployments to use new terms until they become official
private boolean shouldUpdateRevNumber = true; // by default we should update the revision number -not true on synchronization with SAS
/**
* inVocabulary is the parent Authority for this context
abstract public String getParentCommonSchemaName();
+ //
+ // Getter and Setter for 'proposed'
+ //
+ public boolean getIsProposed() {
+ return this.isProposed;
+ }
+
+ public void setIsProposed(boolean flag) {
+ this.isProposed = flag;
+ }
+
+ //
+ // Getter and Setter for 'shouldUpdateRevNumber'
+ //
public boolean getShouldUpdateRevNumber() {
return this.shouldUpdateRevNumber;
}
}
/**
- * This method synchronizes/updates a single authority item resource.
+ * This method synchronizes/updates a single authority item resource with the corresponding SAS items.
+ * The argument 'wrapDoc' in most CSpace methods usually contains a resource payload -like a nuxeo document model. However,
+ * for the handleSync method, the wrapDoc argument contains a authority item specifier.
*/
@Override
public boolean handleSync(DocumentWrapper<Object> wrapDoc) throws Exception {
ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
//
- // Get information about the local authority item so we can compare with corresponding item on the shared authority server
+ // Get information about the local authority item so we can compare with the corresponding item on the shared authority server
//
AuthorityItemSpecifier authorityItemSpecifier = (AuthorityItemSpecifier) wrapDoc.getWrappedObject();
DocumentModel itemDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), getAuthorityItemCommonSchemaName(),
authorityItemSpecifier.getItemSpecifier().value));
}
Long localItemRev = (Long) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.REV);
+ Boolean localIsProposed = (Boolean) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.PROPOSED);
String localItemCsid = itemDocModel.getName();
String localItemWorkflowState = (String) NuxeoUtils.getProperyValue(itemDocModel, CollectionSpaceClient.CORE_WORKFLOWSTATE);
String itemShortId = (String) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.SHORT_IDENTIFIER);
authorityItemSpecifier.getParentSpecifier());
String authorityShortId = (String) NuxeoUtils.getProperyValue(authorityDocModel, AuthorityJAXBSchema.SHORT_IDENTIFIER);
String localParentCsid = authorityDocModel.getName();
+
//
// Using the short IDs of the local authority and item, create URN specifiers to retrieve the SAS authority item
//
Specifier sasAuthoritySpecifier = new Specifier(SpecifierForm.URN_NAME, RefNameUtils.createShortIdRefName(authorityShortId));
Specifier sasItemSpecifier = new Specifier(SpecifierForm.URN_NAME, RefNameUtils.createShortIdRefName(itemShortId));
AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(sasAuthoritySpecifier, sasItemSpecifier);
- // Get the shared authority server's copy
+ // Get the shared authority server's copy of the item/term
PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadIn(sasAuthorityItemSpecifier,
getAuthorityServicePath(), getEntityResponseType());
Long sasRev = getRevision(sasPayloadIn);
String sasWorkflowState = getWorkflowState(sasPayloadIn);
//
- // If the shared authority item is newer, update our local copy
+ // Update the local item if the shared authority item is newer or if the local item is a proposed item that is now becoming a true SAS item
//
- if (sasRev > localItemRev) {
+ if (sasRev > localItemRev || localIsProposed) {
ResourceMap resourceMap = ctx.getResourceMap();
String resourceName = this.getAuthorityServicePath();
AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
PoxPayloadOut payloadOut = authorityResource.updateAuthorityItem(ctx,
resourceMap,
ctx.getUriInfo(),
- localParentCsid, // parent's CSID
- localItemCsid, // item's CSID
- sasPayloadIn, // the payload from the remote SAS
- AuthorityServiceUtils.DONT_UPDATE_REV); // don't update the parent's revision number
+ localParentCsid, // parent's CSID
+ localItemCsid, // item's CSID
+ sasPayloadIn, // the payload from the remote SAS
+ AuthorityServiceUtils.DONT_UPDATE_REV, // don't update the parent's revision number
+ AuthorityServiceUtils.NOT_PROPOSED); // The items is not proposed, make it a real SAS item now
if (payloadOut != null) {
ctx.setOutput(payloadOut);
result = true;
String resourceName = this.getAuthorityServicePath();
AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
//
- // We need to move the local item to the SAS workflow state. This might involve multiple transitions.
+ // We need to move the local item to the SAS workflow state -i.e., the locked state.
//
List<String> transitionList = getTransitionList(sasWorkflowState, localItemWorkflowState);
for (String transition:transitionList) {
}
/**
- * We need to move the local item to the SAS workflow state. This might involve multiple transitions.
+ * We need to move the local item to the SAS item's workflow state. This might involve multiple transitions.
*/
private List<String> getTransitionList(String sasWorkflowState, String localItemWorkflowState) {
List<String> result = new ArrayList<String>();
- // TO BE COMPLETELED
+ //
+ // If the SAS authority items is soft-deleted, we need to mark the local item as soft-deleted
+ //
+ if (sasWorkflowState.contains(WorkflowClient.WORKFLOWSTATE_DELETED) == true) {
+ result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
+ }
+ //
+ // Ensure the local item is in a "locked" state. Items sync'd with a SAS should always be locked
+ //
+ if (localItemWorkflowState.contains(WorkflowClient.WORKFLOWSTATE_LOCKED) != true) {
+ result.add(WorkflowClient.WORKFLOWTRANSITION_LOCK);
+ }
+
return result;
}
// Ensure we have required fields set properly
handleInAuthority(wrapDoc.getWrappedObject());
}
+
+ /* This method ensures that before we delete an authority item, there are no records in the system that reference it.
+ *
+ * (non-Javadoc)
+ * @see org.collectionspace.services.common.document.DocumentHandler#handleDelete(org.collectionspace.services.common.document.DocumentWrapper)
+ */
+ @Override
+ public void handleDelete(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
+ UriTemplateRegistry uriTemplateRegistry = ServiceMain.getInstance().getUriTemplateRegistry();
+ ServiceContext ctx = getServiceContext();
+ DocumentModel docModel = wrapDoc.getWrappedObject();
+ String inAuthorityCsid = (String) docModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.IN_AUTHORITY);
+ String itemCsid = docModel.getName();
+
+ ResourceMap resourceMap = ctx.getResourceMap();
+ String resourceName = this.getAuthorityServicePath();
+ AuthorityResource authorityResource = (AuthorityResource) resourceMap.get(resourceName);
+ //
+ // If the authority item has references to it from other resources, we can't and won't
+ // delete it.
+ //
+ AuthorityRefDocList refObjs = authorityResource.getReferencingObjects(ctx, inAuthorityCsid, itemCsid,
+ uriTemplateRegistry, ctx.getUriInfo());
+ if (refObjs.getTotalItems() != 0) {
+ throw new DocumentException(String.format("Can not delete authority item '%s' because it still has records in the system that are referencing it.",
+ itemCsid));
+ }
+ }
/*
* This method gets called after the primary update to an authority item has happened. If the authority item's refName
}
}
+ //
+ // Handles both update calls (PUTS) AND create calls (POSTS)
+ //
public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
super.fillAllParts(wrapDoc, action);
//
if (this.getShouldUpdateRevNumber() == true) { // We won't update rev numbers on synchronization with SAS
updateRevNumbers(wrapDoc);
}
+ //
+ // If this is a proposed item (not part of the SAS), mark it as such
+ //
+ DocumentModel documentModel = wrapDoc.getWrappedObject();
+ documentModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.PROPOSED,
+ new Boolean(this.getIsProposed()));
}
/**
import java.util.List;
import org.collectionspace.services.jaxb.AbstractCommonList;
+
import org.slf4j.Logger;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public static String ListItemGetCSID(AbstractCommonList.ListItem item) {
return ListItemGetElementValue(item, "csid");
}
-
+
public static String ListItemGetElementValue(AbstractCommonList.ListItem item,
String elName) {
+ String result = null;
+
List<Element> elList = item.getAny();
- for(Element el : elList) {
- if(elName.equalsIgnoreCase(el.getNodeName())) {
+ for (Element el : elList) {
+ if (elName.equalsIgnoreCase(el.getNodeName())) {
Node textEl = el.getFirstChild();
- return textEl.getNodeValue();
+ result = textEl.getNodeValue();
+ break;
}
- }
- return null;
+ }
+
+ return result;
}
-
-
-
}
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
-
+ protected final ServiceContext<IT, OT> NULL_CONTEXT = null;
// Fields for default client factory and client
/** The repository client factory. */
private RepositoryClientFactory<IT, OT> repositoryClientFactory;
/*
* Maps service names to Resource instances. Use the Service Client Class to get the service name.
*/
-public interface ResourceMap extends Map<String, NuxeoBasedResource> {
+public interface ResourceMap extends Map<String, CollectionSpaceResource> {
}
package org.collectionspace.services.common.context;
import java.lang.reflect.Constructor;
+
import javax.ws.rs.core.UriInfo;
+import org.collectionspace.services.common.CollectionSpaceResource;
import org.collectionspace.services.common.ResourceMap;
import org.collectionspace.services.common.ServiceMain;
import org.collectionspace.services.common.config.ConfigUtils;
import org.collectionspace.services.common.security.UnauthorizedException;
import org.collectionspace.services.config.service.ServiceBindingType;
import org.collectionspace.services.config.tenant.TenantBindingType;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
this.output = output;
}
+ /**
+ * Return the JAX-RS resource for the current context.
+ *
+ * @param ctx
+ * @return
+ * @throws Exception
+ */
+ public CollectionSpaceResource<IT, OT> getResource(ServiceContext ctx) throws Exception {
+ CollectionSpaceResource<IT, OT> result = null;
+
+ ResourceMap resourceMap = ctx.getResourceMap();
+ String resourceName = ctx.getClient().getServiceName();
+ result = resourceMap.get(resourceName);
+
+ return result;
+ }
+
/**
* @return the map of service names to resource classes.
*/
import javax.ws.rs.core.UriInfo;
import org.collectionspace.services.client.CollectionSpaceClient;
+import org.collectionspace.services.common.CollectionSpaceResource;
import org.collectionspace.services.common.ResourceMap;
import org.collectionspace.services.common.document.DocumentHandler;
import org.collectionspace.services.common.document.ValidatorHandler;
*/
public void setOutput(OT output);
+ /**
+ *
+ */
+ public CollectionSpaceResource getResource();
+
/**
* @return the map of service names to resource classes.
*/
DocumentModelHandler targetDocHandler = (DocumentModelHandler)ctx.getProperty(WorkflowClient.TARGET_DOCHANDLER);
targetDocHandler.setRepositorySession(this.getRepositorySession()); // Make sure the target doc handler has a repository session to work with
TransitionDef transitionDef = (TransitionDef)ctx.getProperty(WorkflowClient.TRANSITION_ID);
- targetDocHandler.handleWorkflowTransition(wrapDoc, transitionDef); // Call the parent resouce's handler first
+ targetDocHandler.handleWorkflowTransition(wrapDoc, transitionDef); // Call the target resouce's handler first
//
// If no exception occurred, then call the super's method
//