From: Ray Lee Date: Fri, 11 Oct 2019 00:14:59 +0000 (-0700) Subject: DRYD-769: Deprecate items that are in use locally when deleted on a remote server. X-Git-Url: https://git.aero2k.de/?a=commitdiff_plain;h=b175a43d53e359febfd5597d97dd795292bd0f5b;p=tmp%2Fjakarta-migration.git DRYD-769: Deprecate items that are in use locally when deleted on a remote server. --- diff --git a/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java b/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java index efca5dceb..2d3252fbb 100644 --- a/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java +++ b/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java @@ -38,9 +38,9 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; -import javax.ws.rs.core.Response.ResponseBuilder; import org.collectionspace.services.client.IClientQueryParams; import org.collectionspace.services.client.IQueryManager; @@ -49,7 +49,6 @@ import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.client.XmlTools; import org.collectionspace.services.client.workflow.WorkflowClient; - import org.collectionspace.services.common.CSWebApplicationException; import org.collectionspace.services.common.NuxeoBasedResource; import org.collectionspace.services.common.ResourceMap; @@ -79,26 +78,24 @@ import org.collectionspace.services.common.document.DocumentWrapper; import org.collectionspace.services.common.document.Hierarchy; import org.collectionspace.services.common.query.QueryManager; import org.collectionspace.services.common.repository.RepositoryClient; -import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler; -import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler; import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier; -import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm; import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier; - +import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm; +import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler; +import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler; +import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler; import org.collectionspace.services.config.ClientType; import org.collectionspace.services.config.service.ServiceBindingType; +import org.collectionspace.services.description.ServiceDescription; 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.DocumentModelHandler; import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface; +import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler; import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentFilter; import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl; import org.collectionspace.services.nuxeo.util.NuxeoUtils; import org.collectionspace.services.workflow.WorkflowCommon; -import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler; -import org.collectionspace.services.description.ServiceDescription; - import org.jboss.resteasy.util.HttpResponseCodes; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; @@ -934,6 +931,14 @@ public abstract class AuthorityResource return result.getBytes(); } + public PoxPayloadOut updateItemWorkflowWithTransition(ServiceContext existingContext, + String parentIdentifier, + String itemIdentifier, + String transition, + boolean updateRevNumber) throws DocumentReferenceException { + return updateItemWorkflowWithTransition(existingContext, parentIdentifier, itemIdentifier, transition, updateRevNumber, true); + } + /** * Update an authority item's workflow state. * @param existingContext @@ -947,7 +952,8 @@ public abstract class AuthorityResource String parentIdentifier, String itemIdentifier, String transition, - boolean updateRevNumber) throws DocumentReferenceException { + boolean updateRevNumber, + boolean rollbackOnException) throws DocumentReferenceException { PoxPayloadOut result = null; try { @@ -963,6 +969,7 @@ public abstract class AuthorityResource PoxPayloadIn input = new PoxPayloadIn(WorkflowClient.SERVICE_PAYLOAD_NAME, new WorkflowCommon(), WorkflowClient.SERVICE_COMMONPART_NAME); MultipartServiceContext ctx = (MultipartServiceContext) createServiceContext(WorkflowClient.SERVICE_NAME, input); + ctx.setRollbackOnException(rollbackOnException); if (existingContext != null && existingContext.getCurrentRepositorySession() != null) { ctx.setCurrentRepositorySession(existingContext.getCurrentRepositorySession());// If a repo session is already open, we need to use it and not create a new one } @@ -1534,6 +1541,13 @@ public abstract class AuthorityResource return result; } + public boolean deleteAuthorityItem(ServiceContext existingCtx, + String parentIdentifier, + String itemIdentifier, + boolean shouldUpdateRevNumber) throws Exception { + return deleteAuthorityItem(existingCtx, parentIdentifier, itemIdentifier, shouldUpdateRevNumber, true); + } + /** * * @param existingCtx @@ -1544,11 +1558,13 @@ public abstract class AuthorityResource public boolean deleteAuthorityItem(ServiceContext existingCtx, String parentIdentifier, String itemIdentifier, - boolean shouldUpdateRevNumber + boolean shouldUpdateRevNumber, + boolean rollbackOnException ) throws Exception { boolean result = true; ServiceContext ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo()); + ctx.setRollbackOnException(rollbackOnException); if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) { ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); ctx.setProperties(existingCtx.getProperties()); diff --git a/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityServiceUtils.java b/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityServiceUtils.java index 6bc5112c2..e82bb881a 100644 --- a/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityServiceUtils.java +++ b/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityServiceUtils.java @@ -1,5 +1,6 @@ package org.collectionspace.services.common.vocabulary; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -10,15 +11,17 @@ import javax.ws.rs.core.Response; import org.collectionspace.services.client.AuthorityClient; import org.collectionspace.services.client.PoxPayloadIn; +import org.collectionspace.services.client.workflow.WorkflowClient; import org.collectionspace.services.common.ServiceMain; import org.collectionspace.services.common.api.RefNameUtils; import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo; import org.collectionspace.services.common.api.Tools; import org.collectionspace.services.common.context.MultipartServiceContextImpl; import org.collectionspace.services.common.context.ServiceContext; +import org.collectionspace.services.common.document.DocumentException; +import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier; import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier; -import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityIdentifierUtils; import org.collectionspace.services.config.service.ServiceBindingType; import org.collectionspace.services.config.tenant.RemoteClientConfig; import org.collectionspace.services.config.tenant.RemoteClientConfigurations; @@ -29,8 +32,6 @@ import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Node; import org.dom4j.XPath; -import org.collectionspace.services.common.document.DocumentException; -import org.collectionspace.services.common.document.DocumentNotFoundException; import org.nuxeo.ecm.core.api.DocumentModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,6 +51,9 @@ public class AuthorityServiceUtils { public static final boolean UPDATE_REV = true; public static final boolean DONT_UPDATE_REV = !UPDATE_REV; + public static final boolean ROLLBACK_ON_EXCEPTION = true; + public static final boolean DONT_ROLLBACK_ON_EXCEPTION = !ROLLBACK_ON_EXCEPTION; + // 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; @@ -186,19 +190,6 @@ public class AuthorityServiceUtils { return result; } - public static boolean setAuthorityItemDeprecated(ServiceContext ctx, - DocumentModel docModel, String authorityItemCommonSchemaName, Boolean flag) throws Exception { - boolean result = false; - - docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.DEPRECATED, - new Boolean(flag)); - CoreSessionInterface repoSession = (CoreSessionInterface) ctx.getCurrentRepositorySession(); - repoSession.saveDocument(docModel); - result = true; - - return result; - } - /* * The domain name part of refnames on a remote SAS may not match that of local refnames. * Update all the payload's refnames with the local domain name. @@ -350,16 +341,131 @@ public class AuthorityServiceUtils { * @param itemInfo * @throws Exception */ - public static boolean markAuthorityItemAsDeprecated(ServiceContext ctx, String authorityItemCommonSchemaName, AuthorityItemSpecifier authorityItemSpecifier) throws Exception { - boolean result = false; + public static boolean setAuthorityItemDeprecated(ServiceContext ctx, AuthorityResource authorityResource, String authorityItemCommonSchemaName, AuthorityItemSpecifier authorityItemSpecifier) throws Exception { + DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, (CoreSessionInterface)ctx.getCurrentRepositorySession(), + authorityItemCommonSchemaName, authorityItemSpecifier); - try { - DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, (CoreSessionInterface)ctx.getCurrentRepositorySession(), - authorityItemCommonSchemaName, authorityItemSpecifier); - result = setAuthorityItemDeprecated(ctx, docModel, authorityItemCommonSchemaName, AuthorityServiceUtils.DEPRECATED); - } catch (Exception e) { - logger.warn(String.format("Could not mark item '%s' as deprecated.", authorityItemSpecifier.getItemSpecifier().getURNValue()), e); - throw e; + return setAuthorityItemDeprecated(ctx, authorityResource, authorityItemSpecifier.getParentSpecifier().value, authorityItemSpecifier.getItemSpecifier().getURNValue(), docModel); + } + + public static boolean setAuthorityItemDeprecated(ServiceContext ctx, AuthorityResource authorityResource, String parentIdentifier, String itemIdentifier, DocumentModel docModel) throws Exception { + String currentWorkflowState = docModel.getCurrentLifeCycleState(); + + // Find the transitions needed to get from the current state to the replicated deprecated state. + List transitions = AuthorityServiceUtils.getTransitionList(WorkflowClient.WORKFLOWSTATE_DEPRECATED, currentWorkflowState); + + if (!transitions.isEmpty()) { + for (String transition : transitions) { + authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.DONT_UPDATE_REV); + } + } + + return true; + } + + /** + * We need to change the local item's state to one that maps to the replication server's workflow + * state. This might involve making multiple transitions. + * + * WIKI: + * See table at https://collectionspace.atlassian.net/wiki/spaces/SDR/pages/665886940/Workflow+transitions+to+map+SAS+item+states+to+Local+item+states + * (was https://wiki.collectionspace.org/pages/viewpage.action?pageId=162496564) + * + */ + public static List getTransitionList(String sasWorkflowState, String localItemWorkflowState) throws DocumentException { + List result = new ArrayList(); + // + // The first set of conditions maps a replication-server "project" state to a local client state of "replicated" + // + if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); + result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) { + // Do nothing. We're good with this state + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); + // + // The second set of conditions maps a replication-server "deleted" state to a local client state of "deleted" + // + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); + result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) { + // Do nothing. We're good with this state + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); + result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); + // + // The third set of conditions maps a replication-server "replicated" state to a local state of "replicated" + // + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); + result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) { + // Do nothing. We're good with this state + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); + // + // The fourth set of conditions maps a replicatation-server "deprecated" state to a local state of "replicated_deprecated" + // + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); + result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); + result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); + result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); + result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) { + // Do nothing. + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); + // + // The last set of conditions maps a replication-server "replicated_deleted" state to a local client state of "deleted" + // + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); + result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) { + // Do nothing. We're good with this state + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); + result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); + } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) { + result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); + } else { + // + // If we get here, we've encountered a SAS workflow state that we don't recognize. + // + throw new DocumentException(String.format("Encountered an invalid workflow state of '%s' on a SAS authority item.", sasWorkflowState)); } return result; diff --git a/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityDocumentModelHandler.java b/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityDocumentModelHandler.java index 7439209b6..6054afdd7 100644 --- a/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityDocumentModelHandler.java +++ b/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityDocumentModelHandler.java @@ -310,22 +310,18 @@ public abstract class AuthorityDocumentModelHandler extends NuxeoDoc try { authorityResource.deleteAuthorityItem(ctx, parentSpecifier.getURNValue(), - itemSpecifier.getURNValue(), AuthorityServiceUtils.DONT_UPDATE_REV); + itemSpecifier.getURNValue(), AuthorityServiceUtils.DONT_UPDATE_REV, AuthorityServiceUtils.DONT_ROLLBACK_ON_EXCEPTION); handledCount++; } catch (DocumentReferenceException dre) { - logger.info(String.format("Item %s has existing references and cannot be deleted.", itemShortId), dre); + logger.info(String.format("Failed to delete %s: item is referenced, and will be deprecated instead", itemShortId)); AuthorityItemSpecifier authorityItemSpecifier = new AuthorityItemSpecifier(parentSpecifier, itemSpecifier); - boolean deprecated = AuthorityServiceUtils.markAuthorityItemAsDeprecated(ctx, authorityItemCommonSchemaName, authorityItemSpecifier); + boolean deprecated = AuthorityServiceUtils.setAuthorityItemDeprecated(ctx, authorityResource, authorityItemCommonSchemaName, authorityItemSpecifier); if (deprecated == true) { handledCount++; } - } catch (Exception e) { - logger.error(String.format("Unable to delete or deprecate item %s", itemShortId), e); - - throw(e); } } diff --git a/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityItemDocumentModelHandler.java b/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityItemDocumentModelHandler.java index 68c11156e..c3d895e53 100644 --- a/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityItemDocumentModelHandler.java +++ b/services/authority/service/src/main/java/org/collectionspace/services/common/vocabulary/nuxeo/AuthorityItemDocumentModelHandler.java @@ -23,12 +23,21 @@ */ package org.collectionspace.services.common.vocabulary.nuxeo; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import javax.ws.rs.core.MultivaluedMap; + import org.collectionspace.services.client.AuthorityClient; import org.collectionspace.services.client.IQueryManager; -import org.collectionspace.services.client.PayloadInputPart; import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; -import org.collectionspace.services.client.RelationClient; import org.collectionspace.services.client.workflow.WorkflowClient; import org.collectionspace.services.common.api.RefName; import org.collectionspace.services.common.api.Tools; @@ -41,8 +50,8 @@ import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentReferenceException; import org.collectionspace.services.common.document.DocumentWrapper; import org.collectionspace.services.common.repository.RepositoryClient; -import org.collectionspace.services.common.vocabulary.AuthorityJAXBSchema; import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema; +import org.collectionspace.services.common.vocabulary.AuthorityJAXBSchema; import org.collectionspace.services.common.vocabulary.AuthorityResource; import org.collectionspace.services.common.vocabulary.AuthorityServiceUtils; import org.collectionspace.services.common.vocabulary.RefNameServiceUtils; @@ -51,31 +60,18 @@ import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specif import org.collectionspace.services.config.service.ListResultField; import org.collectionspace.services.config.service.ObjectPartType; import org.collectionspace.services.lifecycle.TransitionDef; -import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler; import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface; +import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler; import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl; import org.collectionspace.services.nuxeo.util.NuxeoUtils; import org.collectionspace.services.relation.RelationsCommonList; import org.collectionspace.services.vocabulary.VocabularyItemJAXBSchema; - import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.model.PropertyException; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.ws.rs.core.MultivaluedMap; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - //import org.collectionspace.services.common.authority.AuthorityItemRelations; /** * AuthorityItemDocumentModelHandler @@ -518,133 +514,32 @@ public abstract class AuthorityItemDocumentModelHandler // // Check to see if we need to update the local items's workflow state to reflect that of the remote's // - List transitionList = getTransitionList(sasWorkflowState, localItemWorkflowState); + List transitionList = AuthorityServiceUtils.getTransitionList(sasWorkflowState, localItemWorkflowState); + if (transitionList.isEmpty() == false) { AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath()); // Get the authority (parent) client not the item client // // We need to move the local item to the SAS workflow state. This might involve multiple transitions. // - for (String transition:transitionList) { - try { - authorityResource.updateItemWorkflowWithTransition(ctx, localParentCsid, localItemCsid, transition, AuthorityServiceUtils.DONT_UPDATE_REV); - } catch (DocumentReferenceException de) { - // - // This exception means we tried unsuccessfully to soft-delete (workflow transition 'delete') an item that still has references to it from other records. - // - AuthorityServiceUtils.setAuthorityItemDeprecated(ctx, itemDocModel, authorityItemCommonSchemaName, AuthorityServiceUtils.DEPRECATED); // Since we can't sof-delete it, we need to mark it as deprecated since it is soft-deleted on the SAS - logger.warn(String.format("Could not transition item CSID='%s' from workflow state '%s' to '%s'. Check the services log file for details.", - localItemCsid, localItemWorkflowState, sasWorkflowState)); + try { + for (String transition:transitionList) { + authorityResource.updateItemWorkflowWithTransition(ctx, localParentCsid, localItemCsid, transition, AuthorityServiceUtils.DONT_UPDATE_REV, AuthorityServiceUtils.DONT_ROLLBACK_ON_EXCEPTION); } - } - result = true; - } + } catch (DocumentReferenceException de) { + // + // This exception means we tried unsuccessfully to soft-delete (workflow transition 'delete') an item that still has references to it from other records. + // + logger.info(String.format("Failed to soft-delete %s (transition from %s to %s): item is referenced, and will be deprecated instead", localItemCsid, localItemWorkflowState, sasWorkflowState)); - return result; - } + // One or more of the transitions may have succeeded, so refresh the document model to make sure it + // reflects the current workflow state. + itemDocModel.refresh(); - /** - * We need to change the local item's state to one that maps to the replication server's workflow - * state. This might involve making multiple transitions. - * - * WIKI: - * See table at https://collectionspace.atlassian.net/wiki/spaces/SDR/pages/665886940/Workflow+transitions+to+map+SAS+item+states+to+Local+item+states - * (was https://wiki.collectionspace.org/pages/viewpage.action?pageId=162496564) - * - */ - private List getTransitionList(String sasWorkflowState, String localItemWorkflowState) throws DocumentException { - List result = new ArrayList(); - // - // The first set of conditions maps a replication-server "project" state to a local client state of "replicated" - // - if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); - result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) { - // Do nothing. We're good with this state - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); - // - // The second set of conditions maps a replication-server "deleted" state to a local client state of "deleted" - // - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); - result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) { - // Do nothing. We're good with this state - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); - result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); - // - // The third set of conditions maps a replication-server "replicated" state to a local state of "replicated" - // - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); - result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) { - // Do nothing. We're good with this state - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); - // - // The fourth set of conditions maps a replicatation-server "deprecated" state to a local state of "replicated_deprecated" - // - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); - result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); - result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); - result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); - result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) { - // Do nothing. - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE); - // - // The last set of conditions maps a replication-server "replicated_deleted" state to a local client state of "deleted" - // - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); - result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) { - // Do nothing. We're good with this state - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); - result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE); - } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) { - result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE); - } else { - // - // If we get here, we've encountered a SAS workflow state that we don't recognize. - // - throw new DocumentException(String.format("Encountered an invalid workflow state of '%s' on a SAS authority item.", sasWorkflowState)); + // Since we can't soft-delete it, we need to mark it as deprecated since it is soft-deleted on the SAS. + AuthorityServiceUtils.setAuthorityItemDeprecated(ctx, authorityResource, localParentCsid, localItemCsid, itemDocModel); + } + + result = true; } return result; diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContextImpl.java b/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContextImpl.java index c88323d6a..25caaab68 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContextImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContextImpl.java @@ -34,7 +34,6 @@ import javax.ws.rs.core.Request; import javax.ws.rs.core.UriInfo; import org.collectionspace.authentication.AuthN; -import org.collectionspace.authentication.spi.AuthNContext; import org.collectionspace.services.client.AuthorityClient; import org.collectionspace.services.client.CollectionSpaceClient; import org.collectionspace.services.client.IClientQueryParams; @@ -42,12 +41,11 @@ import org.collectionspace.services.client.IQueryManager; import org.collectionspace.services.client.workflow.WorkflowClient; import org.collectionspace.services.common.ServiceMain; import org.collectionspace.services.common.api.Tools; -import org.collectionspace.services.common.authorization_mgt.AuthorizationCommon; import org.collectionspace.services.common.config.PropertyItemUtils; import org.collectionspace.services.common.config.ServiceConfigUtils; import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl; -import org.collectionspace.services.common.document.DocumentHandler; import org.collectionspace.services.common.document.DocumentFilter; +import org.collectionspace.services.common.document.DocumentHandler; import org.collectionspace.services.common.document.ValidatorHandler; import org.collectionspace.services.common.security.SecurityContext; import org.collectionspace.services.common.security.SecurityContextImpl; @@ -87,7 +85,7 @@ public abstract class AbstractServiceContextImpl /** The logger. */ final Logger logger = LoggerFactory.getLogger(AbstractServiceContextImpl.class); - + /** The properties. */ Map properties = new HashMap(); /** The object part map. */ @@ -116,7 +114,9 @@ public abstract class AbstractServiceContextImpl private Object currentRepositorySession; /** A reference count for the current repository session */ private int currentRepoSesssionRefCount = 0; - + /** Should the current transaction be rolled back when an exception is caught */ + private boolean rollbackOnException = true; + /** * Instantiates a new abstract service context impl. */ @@ -129,9 +129,9 @@ public abstract class AbstractServiceContextImpl /** * Instantiates a new abstract service context impl. - * + * * @param serviceName the service name - * + * * @throws UnauthorizedException the unauthorized exception */ protected AbstractServiceContextImpl(String serviceName, UriInfo uriInfo) throws UnauthorizedException { @@ -180,7 +180,7 @@ public abstract class AbstractServiceContextImpl } } } - + public int getTimeoutParam(UriInfo ui) { int result = DEFAULT_TX_TIMEOUT; @@ -190,9 +190,9 @@ public abstract class AbstractServiceContextImpl if (timeoutString == null) { timeoutString = queryParams.getFirst(IClientQueryParams.IMPORT_TIMOUT_PARAM); } - + if (timeoutString != null) { - try { + try { result = Integer.parseInt(timeoutString); } catch (NumberFormatException e) { logger.warn("Transaction timeout period parameter could not be parsed. The characters in the parameter string must all be decimal digits. The Import service will use the default timeout period instead.", @@ -203,7 +203,7 @@ public abstract class AbstractServiceContextImpl return result; } - + @Override public int getTimeoutSecs() { UriInfo uriInfo = this.getUriInfo(); @@ -217,7 +217,7 @@ public abstract class AbstractServiceContextImpl @Override public boolean shouldUpdateCoreValues() { boolean recordUpdates = true; - + MultivaluedMap queryParams = getQueryParams(); String paramValue = queryParams.getFirst(IClientQueryParams.UPDATE_CORE_VALUES); if (paramValue != null && paramValue.equalsIgnoreCase(Boolean.FALSE.toString())) { // Find our if the caller wants us to record updates @@ -225,10 +225,10 @@ public abstract class AbstractServiceContextImpl } else if (paramValue != null && paramValue.equals(Long.toString(0))) { recordUpdates = false; } - + return recordUpdates; } - + /** * Default value is 'FALSE' * If this returns true, it means that the refname values in referencing objects (records that reference authority or vocabulary terms) will be updated @@ -238,7 +238,7 @@ public abstract class AbstractServiceContextImpl @Override public boolean shouldForceUpdateRefnameReferences() { boolean forceUpdates = false; - + MultivaluedMap queryParams = getQueryParams(); String paramValue = queryParams.getFirst(IClientQueryParams.FORCE_REFNAME_UPDATES); if (paramValue != null && paramValue.equalsIgnoreCase(Boolean.TRUE.toString())) { // Find our if the caller wants us to force refname updates @@ -246,11 +246,11 @@ public abstract class AbstractServiceContextImpl } else if (paramValue != null && paramValue.equals(Long.toString(1))) { forceUpdates = true; } - + return forceUpdates; } - - + + /* (non-Javadoc) * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel() */ @@ -280,9 +280,9 @@ public abstract class AbstractServiceContextImpl /** * Gets the properties for part. - * + * * @param partLabel the part label - * + * * @return the properties for part */ public List getPropertiesForPart(String partLabel) { @@ -327,7 +327,7 @@ public abstract class AbstractServiceContextImpl /** * Gets the common part properties. - * + * * @return the common part properties */ public List getCommonPartProperties() { @@ -436,16 +436,16 @@ public abstract class AbstractServiceContextImpl // object. return (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName(); } - + @Override public String getTenantQualifiedDoctype(String docType) { // If they have not overridden the setting, use the type of the service // object. String result = ServiceBindingUtils.getTenantQualifiedDocType(this.getTenantId(), docType); - + return result; } - + @Override public String getTenantQualifiedDoctype() { String docType = (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName(); @@ -565,9 +565,9 @@ public abstract class AbstractServiceContextImpl /** * Helps to filter for queries that either want to include or exclude documents in deleted workflow states. - * + * * By default, we return *all* objects/records. - * + * * @param queryParams * @return */ @@ -576,14 +576,14 @@ public abstract class AbstractServiceContextImpl String includeDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP); String includeOnlyDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED_QP); // if set to true, it doesn't matter what the value is for 'includeDeleted' - - if (includeOnlyDeleted != null) { + + if (includeOnlyDeleted != null) { if (Tools.isTrue(includeOnlyDeleted)) { // // A value of 'true' for 'includeOnlyDeleted' means we're looking *only* for soft-deleted records/documents. // result = String.format("(ecm:currentLifeCycleState = '%s' OR ecm:currentLifeCycleState = '%s' OR ecm:currentLifeCycleState = '%s')", - WorkflowClient.WORKFLOWSTATE_DELETED, + WorkflowClient.WORKFLOWSTATE_DELETED, WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED, WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED); } @@ -593,24 +593,24 @@ public abstract class AbstractServiceContextImpl // Ensure we don't return soft-deleted records // result = String.format("(ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s')", - WorkflowClient.WORKFLOWSTATE_DELETED, + WorkflowClient.WORKFLOWSTATE_DELETED, WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED, WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED); } return result; } - + /** * Creates the document handler instance. - * + * * @return the document handler - * + * * @throws Exception the exception */ private DocumentHandler createDocumentHandlerInstance() throws Exception { docHandler = ServiceConfigUtils.createDocumentHandlerInstance(tenantBinding, serviceBinding); - + // // The docHandler for a Service can be null, but usually is not. // @@ -631,13 +631,13 @@ public abstract class AbstractServiceContextImpl docFilter.setPagination(queryParameters); String workflowWhereClause = buildWorkflowWhereClause(queryParameters); if (workflowWhereClause != null) { - docFilter.appendWhereClause(workflowWhereClause, IQueryManager.SEARCH_QUALIFIER_AND); - } - + docFilter.appendWhereClause(workflowWhereClause, IQueryManager.SEARCH_QUALIFIER_AND); + } + } docHandler.setDocumentFilter(docFilter); } - + return docHandler; } @@ -671,7 +671,7 @@ public abstract class AbstractServiceContextImpl documentFilter.setPagination(queryParams); return result; } - + /* * If this element is set in the service binding then use it otherwise * assume that asserts are NOT disabled. @@ -682,7 +682,7 @@ public abstract class AbstractServiceContextImpl result = (disableAsserts != null) ? disableAsserts : false; return result; } - + /* (non-Javadoc) * @see org.collectionspace.services.common.context.ServiceContext#getValidatorHandlers() */ @@ -714,7 +714,7 @@ public abstract class AbstractServiceContextImpl valHandlers = handlers; return valHandlers; } - + /** * If one doesn't already exist, use the default properties filename to load a set of properties that * will be used to create an HTTP client to a CollectionSpace instance. @@ -726,33 +726,33 @@ public abstract class AbstractServiceContextImpl if (authorityClient == null) { result = authorityClient = getClient(CollectionSpaceClient.DEFAULT_CLIENT_PROPERTIES_FILENAME); } - + return result; } - + /* * Use the properties filename passed in to load the URL and credentials that will be used * to create a new HTTP client. - * + * * Never uses or resets the this.authorityClient member. Always creates a new HTTP client using * the loaded properties. - * + * * (non-Javadoc) * @see org.collectionspace.services.common.context.ServiceContext#getClient(java.lang.String) */ @Override public AuthorityClient getClient(String clientPropertiesFilename) throws Exception { AuthorityClient result = null; - + Properties inProperties = Tools.loadProperties(clientPropertiesFilename, true); result = getClient(inProperties); - + return result; } - + public AuthorityClient getClient(Properties inProperties) throws Exception { AuthorityClient result = null; - + String authorityClientClazz = getServiceBinding().getClientHandler(); ClassLoader tccl = Thread.currentThread().getContextClassLoader(); authorityClientClazz = authorityClientClazz.trim(); @@ -770,14 +770,14 @@ public abstract class AbstractServiceContextImpl logger.warn(msg); logger.trace(msg, e); } - + return result; } - + @Override public AuthorityClient getClient(RemoteClientConfig remoteClientConfig) throws Exception { AuthorityClient result = null; - + Properties properties = new Properties(); properties.setProperty(AuthorityClient.URL_PROPERTY, remoteClientConfig.getUrl()); properties.setProperty(AuthorityClient.USER_PROPERTY, remoteClientConfig.getUser()); @@ -794,12 +794,12 @@ public abstract class AbstractServiceContextImpl if (tenantName != null) { properties.setProperty(AuthorityClient.TENANT_NAME_PROPERTY, tenantName); } - + result = getClient(properties); - + return result; } - + @Override public void addValidatorHandler(ValidatorHandler validator) throws Exception { if (valHandlers == null) { @@ -832,7 +832,7 @@ public abstract class AbstractServiceContextImpl /* (non-Javadoc) * @see org.collectionspace.services.common.context.ServiceContext#getQueryParams() - * + * * When we first created these services, the RESTEasy query parameters used to be a modifiable map. That changed in a * more recent version of RESTEasy, so we need to make a copy of the params into a modifiable map and return it instead. */ @@ -872,17 +872,17 @@ public abstract class AbstractServiceContextImpl public UriInfo getUriInfo() { return this.uriInfo; } - + @Override public Request getRequestInfo() { return this.requestInfo; } - + @Override public void setRequestInfo(Request requestInfo) { this.requestInfo = requestInfo; } - + /* * We expect the 'currentRepositorySession' member to be set only once per instance. Also, we expect only one open repository session * per HTTP request. We'll log an error if we see more than one attempt to set a service context's current repo session. @@ -900,42 +900,42 @@ public abstract class AbstractServiceContextImpl logger.error(errMsg); throw new Exception(errMsg); } - + currentRepositorySession = repoSession; this.currentRepoSesssionRefCount++; } - + @Override public void clearCurrentRepositorySession() { if (this.currentRepoSesssionRefCount > 0) { currentRepoSesssionRefCount--; } - + if (currentRepoSesssionRefCount == 0) { this.currentRepositorySession = null; } - + if (currentRepoSesssionRefCount < 0) { throw new RuntimeException("Attempted to clear/close a repository session that has already been cleared/closed."); } } - + @Override public Object getCurrentRepositorySession() { // TODO Auto-generated method stub return currentRepositorySession; - } + } - @Override + @Override public RepositoryDomainType getRepositoryDomain() { return repositoryDomain; } - @Override + @Override public void setRepositoryDomain(RepositoryDomainType repositoryDomain) { this.repositoryDomain = repositoryDomain; } - + /** * Check for a query parameter that indicates if we should force a sync even if the revision numbers indicate otherwise. * @return @@ -943,7 +943,7 @@ public abstract class AbstractServiceContextImpl @Override public boolean shouldForceSync() { boolean forceSync = false; - + MultivaluedMap queryParams = getQueryParams(); String paramValue = queryParams.getFirst(IClientQueryParams.FORCE_SYCN); if (paramValue != null && paramValue.equalsIgnoreCase(Boolean.TRUE.toString())) { // Find our if the caller wants us to force refname updates @@ -951,8 +951,17 @@ public abstract class AbstractServiceContextImpl } else if (paramValue != null && paramValue.equals(Long.toString(1))) { forceSync = true; } - + return forceSync; } - + + @Override + public void setRollbackOnException(boolean rollbackOnException) { + this.rollbackOnException = rollbackOnException; + } + + @Override + public boolean isRollbackOnException() { + return this.rollbackOnException; + } } diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java b/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java index 5adce86df..bcdec5065 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java @@ -66,9 +66,9 @@ public interface ServiceContext { /** The Constant PART_COMMON_LABEL. */ public static final String PART_COMMON_LABEL = "common"; /** Used to qualify document types **/ - public static final String TENANT_SUFFIX = "Tenant"; + public static final String TENANT_SUFFIX = "Tenant"; - /** + /** * Tells the TransactionManager to use the default value. The default value can * be set in this file: * services/JaxRsServiceProvider/src/main/webapp/META-INF/context.xml @@ -78,25 +78,25 @@ public interface ServiceContext { * transactionTimeoutSeconds="300"/> * See the following documentation page for more details: * http://docs.oracle.com/javaee/7/api/javax/transaction/TransactionManager.html#setTransactionTimeout(int) - * + * */ public static final int DEFAULT_TX_TIMEOUT = 0; - - /* + + /* * Sets the current/open repository session */ public void setCurrentRepositorySession(Object repoSession) throws Exception; - + /* * Decrements the context's repo session ref count and nulls it if the count becomes 0. */ public void clearCurrentRepositorySession(); - + /* * If a current repository session exists, returns it. */ public Object getCurrentRepositorySession(); - + /** * getSecurityContext is contains security info. for the service layer */ @@ -108,12 +108,12 @@ public interface ServiceContext { * @return */ public boolean shouldUpdateCoreValues(); - + /** * getTimeoutSecs(); */ public int getTimeoutSecs(); - + /** * getUserId get authenticated user's userId */ @@ -149,16 +149,16 @@ public interface ServiceContext { * @return service name */ public String getDocumentType(); - + /** * Returns a tenant qualified document type. - * + * */ public String getTenantQualifiedDoctype(); - + /** * Returns a tenant qualified document type. - * + * */ public String getTenantQualifiedDoctype(String docType); @@ -180,16 +180,16 @@ public interface ServiceContext { * @return repository domain for the tenant */ public String getRepositoryDomainName(); - + /* * The name of the repository/db for the current context */ public String getRepositoryName() throws Exception; - + /* * Get's the name/label used to create the storage container (folder or directory name) */ - public String getRepositoryDomainStorageName(); + public String getRepositoryDomainStorageName(); /** * getRepositoryClientName returns the repository client name as @@ -228,7 +228,7 @@ public interface ServiceContext { * @return the input */ public IT getInput(); - + /** * setInput is used to set request input before starting to * process input data @@ -257,9 +257,9 @@ public interface ServiceContext { * @param map the map of service names to resource instances. */ public void setResourceMap(ResourceMap map); - + /** - * + * * @param jaxsRsRequest - Keep track of the JAX-RS request information */ public void setRequestInfo(Request jaxsRsRequest); @@ -271,14 +271,14 @@ public interface ServiceContext { public Map getPartsMetadata(); /** - * getCommonPartLabel returns label for common part of a service + * getCommonPartLabel returns label for common part of a service * @return label */ public String getCommonPartLabel(); /** * getCommonPartLabel returns label for common part of a specified schema. - * This is useful for sub-resources. + * This is useful for sub-resources. * @return label */ public String getCommonPartLabel(String schemaName); @@ -325,11 +325,11 @@ public interface ServiceContext { /** * Gets the document hanlder. - * + * * @param queryParams the query params - * + * * @return the document hanlder - * + * * @throws Exception the exception */ public DocumentHandler getDocumentHandler(MultivaluedMap queryParams) throws Exception; @@ -350,7 +350,7 @@ public interface ServiceContext { /** * Gets the query params. - * + * * @return the query params */ public MultivaluedMap getQueryParams(); @@ -359,7 +359,7 @@ public interface ServiceContext { /** * Sets the query params. - * + * * @param queryParams the query params */ public void setQueryParams(MultivaluedMap queryParams); @@ -373,20 +373,20 @@ public interface ServiceContext { public void setRepositoryDomain(RepositoryDomainType repositoryDomain); public CollectionSpaceClient getClient() throws Exception; - + public CollectionSpaceClient getClient(String clientProperitesFilename) throws Exception; - + public CollectionSpaceClient getClient(RemoteClientConfig remoteClientConfig) throws Exception; /** * @return the JAX-RS resource of service for the current context. - * @throws Exception + * @throws Exception */ public CollectionSpaceResource getResource() throws Exception; /** * @return the JAX-RS resource of service for the current context. - * @throws Exception + * @throws Exception */ public CollectionSpaceResource getResource( String serviceName) throws Exception; @@ -405,44 +405,44 @@ public interface ServiceContext { public boolean shouldForceSync(); /** - * + * * @return The JAX-RS request information */ Request getRequestInfo(); - + /** - * + * */ public TransactionContext openConnection() throws TransactionException; // Only 1 active connection at a time - + /** - * + * */ public boolean hasActiveConnection(); - + /** - * + * */ public void closeConnection() throws TransactionException; // Assumes there's been a call to getConnection. - + /** - * @throws TransactionException - * + * @throws TransactionException + * */ void setTransactionContext(TransactionContext transactionCtx) throws TransactionException; // For sharing a transaction context with another service context. - + /** - * + * */ public boolean isTransactionContextShared() throws TransactionException; /** - * + * * @return */ TransactionContext getCurrentTransactionContext(); -} - - + public void setRollbackOnException(boolean rollbackOnException); + public boolean isRollbackOnException(); +} diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoRepositoryClientImpl.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoRepositoryClientImpl.java index 461ae716e..4cb45327d 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoRepositoryClientImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoRepositoryClientImpl.java @@ -32,9 +32,13 @@ import java.util.UUID; import javax.sql.rowset.CachedRowSet; import javax.ws.rs.core.MultivaluedMap; -import org.collectionspace.services.lifecycle.TransitionDef; -import org.collectionspace.services.nuxeo.util.CSReindexFulltextRoot; -import org.collectionspace.services.nuxeo.util.NuxeoUtils; +// +// CSPACE-5036 - How to make CMISQL queries from Nuxeo +// +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.apache.chemistry.opencmis.commons.server.CallContext; +import org.apache.chemistry.opencmis.server.impl.CallContextImpl; +import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStreamFactory; import org.collectionspace.services.client.CollectionSpaceClient; import org.collectionspace.services.client.IQueryManager; import org.collectionspace.services.client.PoxPayloadIn; @@ -42,57 +46,50 @@ import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.client.Profiler; import org.collectionspace.services.client.index.IndexClient; import org.collectionspace.services.client.workflow.WorkflowClient; +import org.collectionspace.services.common.CSWebApplicationException; +import org.collectionspace.services.common.ServiceMain; +import org.collectionspace.services.common.api.Tools; +import org.collectionspace.services.common.config.ConfigUtils; +import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl; +import org.collectionspace.services.common.config.TenantBindingUtils; import org.collectionspace.services.common.context.ServiceContext; -import org.collectionspace.services.common.query.QueryContext; -import org.collectionspace.services.common.repository.RepositoryClient; -import org.collectionspace.services.common.storage.JDBCTools; -import org.collectionspace.services.common.storage.PreparedStatementSimpleBuilder; import org.collectionspace.services.common.document.BadRequestException; import org.collectionspace.services.common.document.DocumentException; import org.collectionspace.services.common.document.DocumentFilter; import org.collectionspace.services.common.document.DocumentHandler; -import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentHandler.Action; +import org.collectionspace.services.common.document.DocumentNotFoundException; import org.collectionspace.services.common.document.DocumentWrapper; import org.collectionspace.services.common.document.DocumentWrapperImpl; import org.collectionspace.services.common.document.TransactionException; -import org.collectionspace.services.common.CSWebApplicationException; -import org.collectionspace.services.common.ServiceMain; -import org.collectionspace.services.common.api.Tools; -import org.collectionspace.services.common.config.ConfigUtils; -import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl; -import org.collectionspace.services.common.config.TenantBindingUtils; +import org.collectionspace.services.common.query.QueryContext; +import org.collectionspace.services.common.repository.RepositoryClient; +import org.collectionspace.services.common.storage.JDBCTools; import org.collectionspace.services.common.storage.PreparedStatementBuilder; +import org.collectionspace.services.common.storage.PreparedStatementSimpleBuilder; import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier; import org.collectionspace.services.config.service.ServiceBindingType; -import org.collectionspace.services.config.tenant.TenantBindingType; import org.collectionspace.services.config.tenant.RepositoryDomainType; - -// -// CSPACE-5036 - How to make CMISQL queries from Nuxeo -// -import org.apache.chemistry.opencmis.commons.enums.CmisVersion; -import org.apache.chemistry.opencmis.commons.server.CallContext; -import org.apache.chemistry.opencmis.server.impl.CallContextImpl; -import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStreamFactory; +import org.collectionspace.services.config.tenant.TenantBindingType; +import org.collectionspace.services.lifecycle.TransitionDef; +import org.collectionspace.services.nuxeo.util.CSReindexFulltextRoot; +import org.collectionspace.services.nuxeo.util.NuxeoUtils; import org.nuxeo.common.utils.IdUtils; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; -import org.nuxeo.ecm.core.api.IterableQueryResult; -import org.nuxeo.ecm.core.api.VersioningOption; -import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; import org.nuxeo.ecm.core.api.DocumentRef; import org.nuxeo.ecm.core.api.IdRef; +import org.nuxeo.ecm.core.api.IterableQueryResult; import org.nuxeo.ecm.core.api.PathRef; -import org.nuxeo.runtime.api.Framework; -import org.nuxeo.runtime.transaction.TransactionRuntimeException; +import org.nuxeo.ecm.core.api.VersioningOption; +import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; import org.nuxeo.ecm.core.opencmis.bindings.NuxeoCmisServiceFactory; import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoCmisService; -import org.nuxeo.elasticsearch.api.ElasticSearchAdmin; -import org.nuxeo.elasticsearch.api.ElasticSearchIndexing; -import org.nuxeo.elasticsearch.api.ElasticSearchService; import org.nuxeo.elasticsearch.ElasticSearchComponent; +import org.nuxeo.elasticsearch.api.ElasticSearchService; +import org.nuxeo.runtime.api.Framework; +import org.nuxeo.runtime.transaction.TransactionRuntimeException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -216,10 +213,14 @@ public class NuxeoRepositoryClientImpl implements RepositoryClient