From d45ae3766b7e5aaafe1dc290950d25691896dca2 Mon Sep 17 00:00:00 2001 From: remillet Date: Tue, 24 May 2016 14:29:09 -0700 Subject: [PATCH] CSPACE-6953: Simple hierarchy relations synchronization working. --- .../src/main/resources/log4j.properties | 1 + .../common/vocabulary/AuthorityResource.java | 17 ++- .../vocabulary/AuthorityServiceUtils.java | 2 +- .../nuxeo/AuthorityDocumentModelHandler.java | 94 ++++++------- .../AuthorityItemDocumentModelHandler.java | 131 ++++++++++++++++-- .../services/client/AuthorityClient.java | 10 +- .../services/client/AuthorityClientImpl.java | 16 ++- .../services/client/AuthorityProxy.java | 18 ++- .../services/client/PayloadPart.java | 7 + .../services/client/PoxPayload.java | 63 ++++++++- .../services/client/PoxPayloadOut.java | 42 ------ .../test/AbstractAuthorityServiceTest.java | 53 +++++++ .../sas-collectionspace-client.properties | 8 +- .../context/MultipartServiceContextImpl.java | 2 +- .../common/storage/StorageClient.java | 2 +- .../jpa/JpaRelationshipStorageClient.java | 7 +- .../storage/jpa/JpaStorageClientImpl.java | 7 +- .../vocabulary/RefNameServiceUtils.java | 14 ++ .../java/RemoteDocumentModelHandlerImpl.java | 9 +- .../client/java/RepositoryClientImpl.java | 8 +- .../nuxeo/VocabularyItemValidatorHandler.java | 60 ++++---- 21 files changed, 414 insertions(+), 157 deletions(-) diff --git a/services/JaxRsServiceProvider/src/main/resources/log4j.properties b/services/JaxRsServiceProvider/src/main/resources/log4j.properties index 89b5901f4..36e5757db 100644 --- a/services/JaxRsServiceProvider/src/main/resources/log4j.properties +++ b/services/JaxRsServiceProvider/src/main/resources/log4j.properties @@ -56,6 +56,7 @@ log4j.logger.org.collectionspace=WARN log4j.logger.org.collectionspace.services.nuxeo.client.java.NuxeoClientEmbedded=ERROR log4j.logger.org.collectionspace.services.nuxeo.client.java.RepositoryClientImpl=ERROR log4j.logger.org.collectionspace.services.common.security.SecurityInterceptor=TRACE +log4j.logger.org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl=DEBUG #log4j.logger.org.collectionspace.services.nuxeo.client.java=ERROR #log4j.logger.org.collectionspace.services.common.storage.JDBCTools=ERROR #log4j.logger.org.collectionspace.services.common.profile.CSpaceFilter=ERROR 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 2ea1c6211..760c5acd5 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 @@ -1120,7 +1120,8 @@ public abstract class AuthorityResource * @return * @throws Exception */ - private PoxPayloadOut synchronizeItem( + @SuppressWarnings("unchecked") + private PoxPayloadOut synchronizeItem( ServiceContext ctx, String parentIdentifier, String itemIdentifier, @@ -1170,6 +1171,7 @@ public abstract class AuthorityResource existingCtx.getUriInfo()); if (existingCtx.getCurrentRepositorySession() != null) { ctx.setCurrentRepositorySession(existingCtx.getCurrentRepositorySession()); + } result = synchronizeItem(ctx, parentIdentifier, itemIdentifier, syncHierarchicalRelationships); @@ -1334,12 +1336,12 @@ public abstract class AuthorityResource * @throws Exception */ @SuppressWarnings("rawtypes") - public void deleteAuthorityItem(ServiceContext existingCtx, + public boolean deleteAuthorityItem(ServiceContext existingCtx, String parentIdentifier, String itemIdentifier, boolean shouldUpdateRevNumber ) throws Exception { - Response result = null; + boolean result = true; ServiceContext ctx = createServiceContext(getItemServiceName(), existingCtx.getUriInfo()); if (existingCtx != null && existingCtx.getCurrentRepositorySession() != null) { @@ -1356,10 +1358,11 @@ public abstract class AuthorityResource } String itemCsid = lookupItemCSID(ctx, itemIdentifier, parentcsid, "deleteAuthorityItem(item)", "DELETE_ITEM"); //use itemServiceCtx if it is not null - DocumentHandler handler = createDocumentHandler(ctx); - ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, shouldUpdateRevNumber); // Sometimes we can only soft-delete, so if it is during a sync we dont' update the revision number - - getRepositoryClient(ctx).delete(ctx, itemCsid, handler); + AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler) createDocumentHandler(ctx); + handler.setShouldUpdateRevNumber(shouldUpdateRevNumber); + result = getRepositoryClient(ctx).delete(ctx, itemCsid, handler); + + return result; } @GET 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 c3b72271f..5b293110e 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 @@ -83,7 +83,7 @@ public class AuthorityServiceUtils { ServiceContext parentCtx = new MultipartServiceContextImpl(serviceName); AuthorityClient client = (AuthorityClient) parentCtx.getClient(CollectionSpaceClient.SAS_CLIENT_PROPERTIES_FILENAME); - Response res = client.readNamedItemInNamedAuthority(specifier.getParentSpecifier().getURNValue(), specifier.getItemSpecifier().getURNValue(), + Response res = client.readItem(specifier.getParentSpecifier().getURNValue(), specifier.getItemSpecifier().getURNValue(), AuthorityClient.INCLUDE_DELETED_ITEMS, syncHierarchicalRelationships); try { 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 4a7e7865f..d6e857432 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 @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import org.collectionspace.services.client.AbstractCommonListUtils; @@ -39,6 +40,7 @@ import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.client.XmlTools; import org.collectionspace.services.client.workflow.WorkflowClient; import org.collectionspace.services.common.ResourceMap; +import org.collectionspace.services.common.api.CommonAPI; import org.collectionspace.services.common.api.RefName; import org.collectionspace.services.common.api.RefName.Authority; import org.collectionspace.services.common.api.RefNameUtils; @@ -215,32 +217,27 @@ public abstract class AuthorityDocumentModelHandler if (itemsInLocalAuthority.size() > 0) { ArrayList remainingItems = itemsInLocalAuthority; // now a subset of local items that no longer exist on the SAS, so we need to try to delete them (or mark them as deprecated if they still have records referencing them) // - // We now need to either hard-deleted or deprecate the remaining authorities + // We now need to either hard-delete or deprecate the remaining authorities // - long processed = deleteOrDeprecateItems(ctx, remainingItems); + long processed = deleteOrDeprecateItems(ctx, sasAuthoritySpecifier, remainingItems); if (processed != remainingItems.size()) { throw new Exception("Encountered unexpected exception trying to delete or deprecated authority items during synchronization."); } } // - // We need to synchronize the hierarchy relationships + // Now that we've sync'd all the items, we need to synchronize the hierarchy relationships // - itemList = getItemList(sasPayloadInItemList); // Really need to re-request the sasPayload? I don't think so. - if (itemList != null) { - for (Element e:itemList) { - String remoteRefName = XmlTools.getElementValue(e, "refName"); - itemsInRemoteAuthority.add(remoteRefName); - long status = syncRemoteItemRelationshipsWithLocalItem(ctx, remoteRefName); - if (status == 1) { - created++; - } else if (status == 0) { - synched++; - } else { - alreadySynched++; - } - totalItemsProcessed++; - } - } + for (String itemShortId:itemsInRemoteAuthority) { + long status = syncRemoteItemRelationshipsWithLocalItem(ctx, sasAuthoritySpecifier, itemShortId); + if (status == 1) { + created++; + } else if (status == 0) { + synched++; + } else { + alreadySynched++; + } + totalItemsProcessed++; + } logger.info(String.format("Total number of items processed during sync: %d", totalItemsProcessed)); logger.info(String.format("Number of items synchronized: %d", synched)); @@ -257,40 +254,40 @@ public abstract class AuthorityDocumentModelHandler * @return * @throws Exception */ - private long deleteOrDeprecateItems(ServiceContext ctx, ArrayList refNameList) throws Exception { + @SuppressWarnings("rawtypes") + private long deleteOrDeprecateItems(ServiceContext ctx, Specifier authoritySpecifier, ArrayList itemShortIdList) throws Exception { long result = 0; - + AuthorityItemSpecifier authorityItemSpecificer = null; + ctx.setProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY, false); // Don't update the revision number when we delete or deprecate the item - for (String itemRefName:refNameList) { - AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(itemRefName); - AuthorityItemSpecifier authorityItemSpecificer = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authorityTermInfo.inAuthority.name, - authorityTermInfo.name); - + for (String itemShortId:itemShortIdList) { AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(); try { + authorityItemSpecificer = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authoritySpecifier.value, + itemShortId); authorityResource.deleteAuthorityItem(ctx, - Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name), - Specifier.createShortIdURNValue(authorityTermInfo.name), + authorityItemSpecificer.getParentSpecifier().getURNValue(), + authorityItemSpecificer.getItemSpecifier().getURNValue(), AuthorityServiceUtils.DONT_UPDATE_REV); // Since we're sync'ing, we shouldn't update the revision number (obviously this only applies to soft-deletes since hard-deletes destroy the record) result++; } catch (DocumentReferenceException de) { - logger.info(String.format("Authority item '%s' has existing references and cannot be removed during sync.", - itemRefName), de); + logger.info(String.format("Authority item with '%s' has existing references and cannot be removed during sync.", + authorityItemSpecificer), de); boolean marked = AuthorityServiceUtils.markAuthorityItemAsDeprecated(ctx, authorityItemCommonSchemaName, authorityItemSpecificer); if (marked == true) { result++; } } catch (Exception e) { - logger.warn(String.format("Unable to delete authority item '%s'", itemRefName), e); + logger.warn(String.format("Unable to delete authority item '%s'", authorityItemSpecificer), e); throw e; } } if (logger.isWarnEnabled() == true) { - if (result != refNameList.size()) { + if (result != itemShortIdList.size()) { logger.warn(String.format("Unable to delete or deprecate some authority items during synchronization with SAS. Deleted or deprecated %d of %d. See the services log file for details.", - result, refNameList.size())); + result, itemShortIdList.size())); } } @@ -350,7 +347,7 @@ public abstract class AuthorityDocumentModelHandler * @param itemIdentifier - Must be in short-id-refname form -i.e., urn:cspace:name(shortid) * @throws Exception */ - protected void createLocalItem(ServiceContext ctx, String parentIdentifier, String itemIdentifier) throws Exception { + protected void createLocalItem(ServiceContext ctx, String parentIdentifier, String itemIdentifier, Boolean syncHierarchicalRelationships) throws Exception { // // Create a URN short ID specifier for the getting a copy of the remote authority item // @@ -361,7 +358,7 @@ public abstract class AuthorityDocumentModelHandler // Get the remote payload // PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadIn(sasAuthorityItemSpecifier, - ctx.getServiceName(), getEntityResponseType()); + ctx.getServiceName(), getEntityResponseType(), syncHierarchicalRelationships); sasPayloadIn = AuthorityServiceUtils.filterRefnameDomains(ctx, sasPayloadIn); // We need to filter domain name part of any and all refnames in the payload // // Using the payload from the remote server, create a local copy of the item @@ -415,7 +412,7 @@ public abstract class AuthorityDocumentModelHandler // Document not found, means we need to create an 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.", itemRefName)); - createLocalItem(ctx, parentIdentifier, itemIdentifier); + createLocalItem(ctx, parentIdentifier, itemIdentifier, AuthorityClient.DONT_INCLUDE_RELATIONS); return 1; // exit with status of 1 means we created a new authority item } // @@ -447,41 +444,32 @@ public abstract class AuthorityDocumentModelHandler * @return * @throws Exception */ - protected long syncRemoteItemRelationshipsWithLocalItem(ServiceContext ctx, String itemRefName) throws Exception { + protected long syncRemoteItemRelationshipsWithLocalItem(ServiceContext ctx, Specifier authoritySpecifier, String itemShortId) throws Exception { long result = -1; - // - // WARNING: THIS CODE IS NOT IMPLEMENTED YET - // - if (result == -1) return result; - // - // Using the item refname (with no local CSID), create specifiers that we'll use to find the local versions - // - - AuthorityTermInfo authorityTermInfo = RefNameUtils.parseAuthorityTermInfo(itemRefName); - String parentIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.inAuthority.name); - String itemIdentifier = Specifier.createShortIdURNValue(authorityTermInfo.name); + String parentIdentifier = authoritySpecifier.getURNValue(); + String itemIdentifier = Specifier.createShortIdURNValue(itemShortId); // // We'll use the Authority JAX-RS resource to peform sync operations (creates and updates) // AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(); PoxPayloadOut localItemPayloadOut; try { + MultivaluedMap queryParams = ctx.getQueryParams(); localItemPayloadOut = authorityResource.getAuthorityItemWithExistingContext(ctx, parentIdentifier, itemIdentifier); } catch (DocumentNotFoundException dnf) { // // Document not found, means we need to create an 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.", itemRefName)); - createLocalItem(ctx, parentIdentifier, itemIdentifier); - return 1; // exit with status of 1 means we created a new authority item + logger.info(String.format("Remote item with short ID ='%s' doesn't exist locally, so we can't synchronize its relationships.", itemShortId)); + return result; } // - // 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 the hierarchy relationships. // // try { - PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, true); + PoxPayloadOut theUpdate = authorityResource.synchronizeItemWithExistingContext(ctx, parentIdentifier, itemIdentifier, AuthorityClient.INCLUDE_RELATIONS); if (theUpdate != null) { result = 0; // means we needed to sync this item with SAS logger.debug(String.format("Sync'd authority item parent='%s' id='%s with SAS. Updated payload is: \n%s", 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 38cff706a..c924de9a8 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 @@ -25,8 +25,10 @@ package org.collectionspace.services.common.vocabulary.nuxeo; 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.ServiceMain; import org.collectionspace.services.common.UriTemplateRegistry; @@ -61,10 +63,12 @@ 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; @@ -415,14 +419,118 @@ public abstract class AuthorityItemDocumentModelHandler } } } - + /** - * This method synchronizes/updates a single authority item resource. - * for the handleSync method, the wrapDoc argument contains a authority item specifier. + * + * @param wrapDoc + * @return + * @throws Exception */ + protected boolean handleRelationsSync(DocumentWrapper wrapDoc) throws Exception { + boolean result = false; + ServiceContext ctx = getServiceContext(); + + // + // Get information about the local authority item so we can compare with corresponding item on the shared authority server + // + AuthorityItemSpecifier authorityItemSpecifier = (AuthorityItemSpecifier) wrapDoc.getWrappedObject(); + DocumentModel itemDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), getAuthorityItemCommonSchemaName(), + authorityItemSpecifier); + if (itemDocModel == null) { + throw new DocumentNotFoundException(String.format("Could not find authority item resource with CSID='%s'", + 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 = itemDocModel.getCurrentLifeCycleState(); + String itemShortId = (String) NuxeoUtils.getProperyValue(itemDocModel, AuthorityItemJAXBSchema.SHORT_IDENTIFIER); + + // + // Now get the item's Authority (the parent) information + // + DocumentModel authorityDocModel = NuxeoUtils.getDocFromSpecifier(ctx, getRepositorySession(), authorityCommonSchemaName, + 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 and retrieve the SAS authority item + // + AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authorityShortId, itemShortId); + // Get the shared authority server's copy + PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadIn(sasAuthorityItemSpecifier, + getAuthorityServicePath(), getEntityResponseType(), AuthorityClient.INCLUDE_RELATIONS); + Long sasRev = getRevision(sasPayloadIn); + String sasWorkflowState = getWorkflowState(sasPayloadIn); + + // + // Get the RelationsCommonList and remove the CSIDs since they are for remote items only. We'll use + // the refnames in the payload instead to find the local CSIDs + // + PayloadInputPart relationsCommonListPart = sasPayloadIn.getPart(RelationClient.SERVICE_COMMON_LIST_NAME); + relationsCommonListPart.clearElementBody(); // clear the existing DOM element that was created from the incoming XML payload + RelationsCommonList rcl = (RelationsCommonList) relationsCommonListPart.getBody(); // Get the JAX-B object and clear the CSID values + for (RelationsCommonList.RelationListItem listItem : rcl.getRelationListItem()) { + // clear the remote relation item's CSID + listItem.setCsid(null); + // clear the remote subject's CSID + listItem.setSubjectCsid(null); + listItem.getSubject().setCsid(null); + listItem.getSubject().setUri(null); + // clear the remote object's CSID + listItem.setObjectCsid(null); + listItem.getObject().setCsid(null); + listItem.getObject().setUri(null); + } + + // + // Remove all the payload parts except the relations part since we only want to sync the relationships + // + ArrayList newPartList = new ArrayList(); + newPartList.add(relationsCommonListPart); // add our CSID filtered RelationsCommonList part + sasPayloadIn.setParts(newPartList); + sasPayloadIn = new PoxPayloadIn(sasPayloadIn.toXML()); // Builds a new payload using the current set of parts -i.e., just the relations part + + sasPayloadIn = AuthorityServiceUtils.filterRefnameDomains(ctx, sasPayloadIn); // We need to filter the domain name part of any and all refnames in the payload + AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath()); + PoxPayloadOut payloadOut = authorityResource.updateAuthorityItem(ctx, + ctx.getResourceMap(), + 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 + AuthorityServiceUtils.NOT_PROPOSED, // The items is not proposed, make it a real SAS item now + AuthorityServiceUtils.SAS_ITEM); // Since we're sync'ing, this must be a SAS item + if (payloadOut != null) { + ctx.setOutput(payloadOut); + result = true; + } + + return result; + } + @Override public boolean handleSync(DocumentWrapper wrapDoc) throws Exception { boolean result = false; + + if (this.getShouldSyncHierarchicalRelationships() == true) { + result = handleRelationsSync(wrapDoc); + } else { + result = handlePayloadSync(wrapDoc); + } + + return result; + } + + /** + * + * @param wrapDoc + * @return + * @throws Exception + */ + protected boolean handlePayloadSync(DocumentWrapper wrapDoc) throws Exception { + boolean result = false; ServiceContext ctx = getServiceContext(); // @@ -454,7 +562,7 @@ public abstract class AuthorityItemDocumentModelHandler AuthorityItemSpecifier sasAuthorityItemSpecifier = new AuthorityItemSpecifier(SpecifierForm.URN_NAME, authorityShortId, itemShortId); // Get the shared authority server's copy PoxPayloadIn sasPayloadIn = AuthorityServiceUtils.requestPayloadIn(sasAuthorityItemSpecifier, - getAuthorityServicePath(), getEntityResponseType()); + getAuthorityServicePath(), getEntityResponseType(), AuthorityClient.DONT_INCLUDE_RELATIONS); Long sasRev = getRevision(sasPayloadIn); String sasWorkflowState = getWorkflowState(sasPayloadIn); // @@ -500,9 +608,6 @@ public abstract class AuthorityItemDocumentModelHandler } result = true; } - // - // We need to synchronize the hierarchy relationships here. - // return result; } @@ -613,16 +718,22 @@ public abstract class AuthorityItemDocumentModelHandler // // If all the refs are to soft-deleted objects, we should soft-delete this authority item instead of hard-deleting it and instead of failing. // - Boolean shouldUpdateRev = (Boolean) ctx.getProperty(AuthorityServiceUtils.SHOULD_UPDATE_REV_PROPERTY); String parentCsid = (String) NuxeoUtils.getProperyValue(docModel, AuthorityItemJAXBSchema.IN_AUTHORITY); String itemCsid = docModel.getName(); AuthorityResource authorityResource = (AuthorityResource) ctx.getResource(getAuthorityServicePath()); authorityResource.updateItemWorkflowWithTransition(ctx, parentCsid, itemCsid, WorkflowClient.WORKFLOWTRANSITION_DELETE, - shouldUpdateRev != null ? shouldUpdateRev : true); - result = false; // Don't delete since we just soft-deleted it. + this.getShouldUpdateRevNumber()); + result = false; // Don't delete since we just soft-deleted it. } } + // + // Since we've changed the state of the parent by deleting (or soft-deleting) one of its items, we might need to update the parent rev number + // + if (getShouldUpdateRevNumber() == true) { + updateRevNumbers(wrapDoc); + } + return result; } diff --git a/services/client/src/main/java/org/collectionspace/services/client/AuthorityClient.java b/services/client/src/main/java/org/collectionspace/services/client/AuthorityClient.java index d95b94480..a1fe2043b 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/AuthorityClient.java +++ b/services/client/src/main/java/org/collectionspace/services/client/AuthorityClient.java @@ -22,6 +22,7 @@ public interface AuthorityClient String parentcsid, diff --git a/services/client/src/main/java/org/collectionspace/services/client/AuthorityProxy.java b/services/client/src/main/java/org/collectionspace/services/client/AuthorityProxy.java index dec6f470d..0648c0ea7 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/AuthorityProxy.java +++ b/services/client/src/main/java/org/collectionspace/services/client/AuthorityProxy.java @@ -34,7 +34,9 @@ public interface AuthorityProxy extends CollectionSpaceCommonListPoxProxy { @Path("/{vcsid}/items/{csid}") Response readItem(@PathParam("vcsid") String vcsid, @PathParam("csid") String csid, - @QueryParam(WorkflowClient.WORKFLOWSTATE_QUERY) String workflowState); + @QueryParam(WorkflowClient.WORKFLOWSTATE_QUERY) String workflowState, + @QueryParam(CommonAPI.showRelations_QP) String showRelations); + //(U)pdate Item @PUT @@ -44,14 +46,24 @@ public interface AuthorityProxy extends CollectionSpaceCommonListPoxProxy { //(U)pdate Item @PUT @Path("/urn:cspace:name({specifier})/items/urn:cspace:name({itemspecifier})") - Response updateNamedItemInNamedAuthority(@PathParam("specifier")String specifier, - @PathParam("itemspecifier")String itemspecifier, byte[] xmlPayload); + Response updateNamedItemInNamedAuthority( + @PathParam("specifier")String specifier, + @PathParam("itemspecifier")String itemspecifier, + byte[] xmlPayload); //(D)elete Item @DELETE @Path("/{vcsid}/items/{csid}") Response deleteItem(@PathParam("vcsid") String vcsid, @PathParam("csid") String csid); + //(D)elete Item + @DELETE + @Path("/urn:cspace:name({specifier})/items/urn:cspace:name({itemspecifier})") + public Response deleteNamedItemInNamedAuthority( + @PathParam("specifier")String specifier, + @PathParam("itemspecifier")String itemspecifier); + + /** * Get a list of objects that reference a given authority term. * diff --git a/services/client/src/main/java/org/collectionspace/services/client/PayloadPart.java b/services/client/src/main/java/org/collectionspace/services/client/PayloadPart.java index 2ba2f68ca..73129346a 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/PayloadPart.java +++ b/services/client/src/main/java/org/collectionspace/services/client/PayloadPart.java @@ -47,6 +47,13 @@ public abstract class PayloadPart { abstract public String asXML(); + /** + * Clear the root DOM element + */ + public void clearElementBody() { + elementBody = null; + } + public Element asElement() { Element result = elementBody; // if we don't already have an Element, let's try to create one from a JAXB object diff --git a/services/client/src/main/java/org/collectionspace/services/client/PoxPayload.java b/services/client/src/main/java/org/collectionspace/services/client/PoxPayload.java index 212277d30..76577afe9 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/PoxPayload.java +++ b/services/client/src/main/java/org/collectionspace/services/client/PoxPayload.java @@ -15,8 +15,8 @@ import javax.xml.bind.Unmarshaller; import javax.xml.transform.stream.StreamSource; import com.sun.xml.bind.api.impl.NameConverter; -import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FileUtils; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; @@ -72,6 +72,48 @@ public abstract class PoxPayload { parseParts(); } + /** + * Creates and returns an XML string representation of ourself. + * + * @return the string + */ + public String toXML() { + String result = null; + Document document = createDOMFromParts(); + + result = document.asXML(); + + if (logger.isTraceEnabled() == true) { + logger.trace("\n\n<<<< Payload : BEGIN <<<<\n" + result + "\n<<<< Payload : END <<<<\n"); + } + + return result; + } + + protected Document createDOMFromParts() { + Document result = null; + + Document document = DocumentHelper.createDocument(); + document.setXMLEncoding("UTF-8"); + document.setName(getName()); + Element root = document.addElement( "document" ); + root.addAttribute("name", getName()); + + Iterator it = getParts().iterator(); + while (it.hasNext() == true) { + PT outPart = it.next(); + Element element = outPart.asElement(); + if (element != null) { + root.add(element.detach()); + } else { + //Add if (logger.isTraceEnabled() == true) logger.trace("Output part: " + outPart.getLabel() + " was empty."); + } + } + result = document; + + return result; + } + /** * Instantiates a new PoxPayload by parsing the payload into a DOM4j * Document instance @@ -197,6 +239,15 @@ public abstract class PoxPayload { public List getParts() { return parts; } + + /** + * Set a new set of parts. + * + * @param newParts + */ + public void setParts(ArrayList newParts) { + this.parts = newParts; + } /** * Adds a POX part to the list of existing parts with the label 'label'. @@ -219,7 +270,15 @@ public abstract class PoxPayload { public PT addPart(PT entity) { parts.add(entity); return entity; - } + } + + /** + * Removes a POX part from our list of parts + * @param entity + */ + public void removePart(PT entity) { + parts.remove(entity); + } /** * Gets the Java package name from the specified namespace. This method diff --git a/services/client/src/main/java/org/collectionspace/services/client/PoxPayloadOut.java b/services/client/src/main/java/org/collectionspace/services/client/PoxPayloadOut.java index 83583b4ed..082f7d7b9 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/PoxPayloadOut.java +++ b/services/client/src/main/java/org/collectionspace/services/client/PoxPayloadOut.java @@ -75,49 +75,7 @@ public class PoxPayloadOut extends PoxPayload { public String asXML() { return toXML(); } - - /** - * Creates and returns an XML string representation of ourself. - * - * @return the string - */ - public String toXML() { - String result = null; - Document document = createDOMFromParts(); - - result = document.asXML(); - - if (logger.isTraceEnabled() == true) { - logger.trace("\n\n<<<< Payload Out : BEGIN <<<<\n" + result + "\n<<<< Payload Out : END <<<<\n"); - } - - return result; - } - - private Document createDOMFromParts() { - Document result = null; - - Document document = DocumentHelper.createDocument(); - document.setXMLEncoding("UTF-8"); - document.setName(getName()); - Element root = document.addElement( "document" ); - root.addAttribute("name", getName()); - - Iterator it = getParts().iterator(); - while (it.hasNext() == true) { - PayloadOutputPart outPart = it.next(); - Element element = outPart.asElement(); - if (element != null) { - root.add(element.detach()); - } else { - //Add if (logger.isTraceEnabled() == true) logger.trace("Output part: " + outPart.getLabel() + " was empty."); - } - } - result = document; - return result; - } - /** * Gets the DOM object that we created at init time. This should never be null; * diff --git a/services/client/src/main/java/org/collectionspace/services/client/test/AbstractAuthorityServiceTest.java b/services/client/src/main/java/org/collectionspace/services/client/test/AbstractAuthorityServiceTest.java index 5459dce86..99a5acad7 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/test/AbstractAuthorityServiceTest.java +++ b/services/client/src/main/java/org/collectionspace/services/client/test/AbstractAuthorityServiceTest.java @@ -872,6 +872,59 @@ public abstract class AbstractAuthorityServiceTest extends JpaStorageClientImpl { * @throws DocumentException */ @Override - public void delete(ServiceContext ctx, String id, DocumentHandler handler) + public boolean delete(ServiceContext ctx, String id, DocumentHandler handler) throws DocumentNotFoundException, DocumentException { - + boolean result = true; + if (ctx == null) { throw new IllegalArgumentException( "delete : ctx is missing"); @@ -431,6 +432,8 @@ public class JpaRelationshipStorageClient extends JpaStorageClientImpl { JpaStorageUtils.releaseEntityManagerFactory(emf); } } + + return result; } /** diff --git a/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageClientImpl.java b/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageClientImpl.java index 560c355ee..759719ffb 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageClientImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/storage/jpa/JpaStorageClientImpl.java @@ -458,8 +458,10 @@ public class JpaStorageClientImpl implements StorageClient { * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String) */ @Override - public void delete(ServiceContext ctx, String id, DocumentHandler handler) + public boolean delete(ServiceContext ctx, String id, DocumentHandler handler) throws DocumentNotFoundException, DocumentException { + boolean result = true; + if (ctx == null) { throw new IllegalArgumentException( "delete(ctx, ix, handler): ctx is missing"); @@ -468,6 +470,7 @@ public class JpaStorageClientImpl implements StorageClient { throw new IllegalArgumentException( "delete(ctx, ix, handler): handler is missing"); } + EntityManagerFactory emf = null; EntityManager em = null; try { @@ -510,6 +513,8 @@ public class JpaStorageClientImpl implements StorageClient { JpaStorageUtils.releaseEntityManagerFactory(emf); } } + + return result; } /** diff --git a/services/common/src/main/java/org/collectionspace/services/common/vocabulary/RefNameServiceUtils.java b/services/common/src/main/java/org/collectionspace/services/common/vocabulary/RefNameServiceUtils.java index 6db4c8d15..e692420dc 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/vocabulary/RefNameServiceUtils.java +++ b/services/common/src/main/java/org/collectionspace/services/common/vocabulary/RefNameServiceUtils.java @@ -210,6 +210,20 @@ public class RefNameServiceUtils { public Specifier getItemSpecifier() { return this.itemSpecifier; } + + @Override + public String toString() { + String result = "%s/items/%s"; + + try { + result = String.format(result, this.parentSpecifier.getURNValue(), this.itemSpecifier.getURNValue()); + } catch (Exception e) { + result = "Unknown error trying to get string representation of Specifier."; + logger.error(result, e); + } + + return result; + } } public static class AuthRefConfigInfo { diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java index 94e0d3533..a135db4ed 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RemoteDocumentModelHandlerImpl.java @@ -1229,6 +1229,13 @@ public abstract class RemoteDocumentModelHandlerImpl if (logger.isTraceEnabled()) { logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID + " got existing relations."); } + } else { + // For debugging purpose only. + if (logger.isDebugEnabled()) { + String debugMsg = "AuthItemDocHndler.updateRelations for: " + itemCSID + " with an fUpdate value of FALSE."; + logger.debug(debugMsg); + throw new Exception(debugMsg); + } } for (RelationsCommonList.RelationListItem inboundItem : inboundList) { @@ -1309,7 +1316,7 @@ public abstract class RemoteDocumentModelHandlerImpl if (logger.isTraceEnabled()) { logger.trace("AuthItemDocHndler.updateRelations for: " + itemCSID + " done."); } - //We return all elements on the inbound list, since we have just worked to make them exist in the system + // We return all elements on the inbound list, since we have just worked to make them exist in the system // and be non-redundant, etc. That list came from relationsCommonListBody, so it is still attached to it, just pass that back. return relationsCommonListBody; } diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryClientImpl.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryClientImpl.java index 2529c99ec..45e993869 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryClientImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryClientImpl.java @@ -1595,8 +1595,10 @@ public class RepositoryClientImpl implements RepositoryClient