diff --git a/source/java/org/alfresco/repo/transfer/DeltaList.java b/source/java/org/alfresco/repo/transfer/DeltaList.java index 924672396f..5c49e7f845 100644 --- a/source/java/org/alfresco/repo/transfer/DeltaList.java +++ b/source/java/org/alfresco/repo/transfer/DeltaList.java @@ -31,18 +31,18 @@ import java.util.TreeSet; public class DeltaList { /** - * The set of requiredURLs + * The set of requiredParts */ - private TreeSet requiredURLs = new TreeSet(); + private TreeSet requiredParts = new TreeSet(); /** * get the list of URLs reqired by the manifest. * @return the list of required URLs */ - public Set getRequiredURLs() + public Set getRequiredParts() { - return requiredURLs; + return requiredParts; } } diff --git a/source/java/org/alfresco/repo/transfer/RepoPrimaryManifestProcessorImpl.java b/source/java/org/alfresco/repo/transfer/RepoPrimaryManifestProcessorImpl.java index 483144c13b..17fb3e31ba 100644 --- a/source/java/org/alfresco/repo/transfer/RepoPrimaryManifestProcessorImpl.java +++ b/source/java/org/alfresco/repo/transfer/RepoPrimaryManifestProcessorImpl.java @@ -290,7 +290,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB Map props = new HashMap(node.getProperties()); // Split out the content properties and sanitise the others - Map contentProps = processProperties(null, props, true); + Map contentProps = processProperties(null, props, null); // inject transferred property here if(!contentProps.containsKey(TransferModel.PROP_REPOSITORY_ID)) @@ -390,6 +390,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB // We need to process content properties separately. // First, create a shallow copy of the supplied property map... Map props = new HashMap(node.getProperties()); + Map existingProps = nodeService.getProperties(nodeToUpdate); // inject transferred property here if(!props.containsKey(TransferModel.PROP_REPOSITORY_ID)) @@ -399,14 +400,14 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB } // Split out the content properties and sanitise the others - Map contentProps = processProperties(nodeToUpdate, props, false); + Map contentProps = processProperties(nodeToUpdate, props, existingProps); // Update the non-content properties nodeService.setProperties(nodeToUpdate, props); // Deal with the content properties writeContent(nodeToUpdate, contentProps); - + // Blend the aspects together Set suppliedAspects = new HashSet(node.getAspects()); Set existingAspects = nodeService.getAspects(nodeToUpdate); @@ -448,17 +449,19 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB * @param nodeToUpdate * The noderef of the existing node in the local repo that is to be updated with these properties. May be * null, indicating that these properties are destined for a brand new local node. - * @param props - * @return A map containing the content properties from the supplied "props" map + * @param props the new properties + * @param the existing properties, null if this is a create + * @return A map containing the content properties which are going to be replaced from the supplied "props" map */ private Map processProperties(NodeRef nodeToUpdate, Map props, - boolean isNew) + Map existingProps) { Map contentProps = new HashMap(); // ...and copy any supplied content properties into this new map... for (Map.Entry propEntry : props.entrySet()) { Serializable value = propEntry.getValue(); + QName key = propEntry.getKey(); if (log.isDebugEnabled()) { if (value == null) @@ -468,7 +471,42 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB } if ((value != null) && ContentData.class.isAssignableFrom(value.getClass())) { - contentProps.put(propEntry.getKey(), propEntry.getValue()); + if(existingProps != null) + { + // This is an update and we have content data + File stagingDir = getStagingFolder(); + ContentData contentData = (ContentData) propEntry.getValue(); + String contentUrl = contentData.getContentUrl(); + String fileName = TransferCommons.URLToPartName(contentUrl); + File stagedFile = new File(stagingDir, fileName); + if (stagedFile.exists()) + { + if(log.isDebugEnabled()) + { + log.debug("replace content for node:" + nodeToUpdate + ", " + key); + } + // Yes we are going to replace the content item + contentProps.put(propEntry.getKey(), propEntry.getValue()); + } + else + { + // Staging file does not exist + if(props.containsKey(key)) + { + if(log.isDebugEnabled()) + { + log.debug("keep existing content for node:" + nodeToUpdate + ", " + key); + } + // keep the existing content value + props.put(propEntry.getKey(), existingProps.get(key)); + } + } + } + else + { + // This is a create so all content items are new + contentProps.put(propEntry.getKey(), propEntry.getValue()); + } } } @@ -480,13 +518,10 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB props.remove(contentPropertyName); } - if (!isNew) + if (existingProps != null) { // Finally, overlay the repo-specific properties from the existing // node (if there is one) - Map existingProps = (nodeToUpdate == null) ? new HashMap() - : nodeService.getProperties(nodeToUpdate); - for (QName localProperty : getLocalProperties()) { Serializable existingValue = existingProps.get(localProperty); @@ -515,7 +550,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB { ContentData contentData = (ContentData) contentEntry.getValue(); String contentUrl = contentData.getContentUrl(); - String fileName = contentUrl.substring(contentUrl.lastIndexOf('/') + 1); + String fileName = TransferCommons.URLToPartName(contentUrl); File stagedFile = new File(stagingDir, fileName); if (!stagedFile.exists()) { diff --git a/source/java/org/alfresco/repo/transfer/RepoRequsiteManifestProcessorImpl.java b/source/java/org/alfresco/repo/transfer/RepoRequsiteManifestProcessorImpl.java index 8ff52005aa..3708e44842 100644 --- a/source/java/org/alfresco/repo/transfer/RepoRequsiteManifestProcessorImpl.java +++ b/source/java/org/alfresco/repo/transfer/RepoRequsiteManifestProcessorImpl.java @@ -22,6 +22,7 @@ package org.alfresco.repo.transfer; import java.io.OutputStream; import java.io.Serializable; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -105,9 +106,9 @@ public class RepoRequsiteManifestProcessorImpl extends AbstractManifestProcessor */ NodeRef destinationNode = resolvedNodes.resolvedChild; -// Serializable yy = node.getProperties().get(ContentModel.PROP_MODIFIED); Map destProps = nodeService.getProperties(destinationNode); -// Serializable xx = destProps.get(ContentModel.PROP_MODIFIED); + + for (Map.Entry propEntry : node.getProperties().entrySet()) { @@ -128,26 +129,33 @@ public class RepoRequsiteManifestProcessorImpl extends AbstractManifestProcessor ContentData destContent = (ContentData)destProps.get(propEntry.getKey()); /** - * If the URLs are the same then the content is already on the server + * If the modification dates for the node are different */ - if(TransferCommons.URLToPartName(destContent.getContentUrl()).equalsIgnoreCase( - TransferCommons.URLToPartName(srcContent.getContentUrl()))) + Serializable srcModified = node.getProperties().get(ContentModel.PROP_MODIFIED); + Serializable destModified = destProps.get(ContentModel.PROP_MODIFIED); + + log.debug ("srcModified :" + srcModified + "destModified :" + destModified); + + if(srcModified != null && + destModified != null && + srcModified instanceof Date && + destModified instanceof Date && + ((Date)srcModified).getTime() >= ((Date)destModified).getTime()) { if(log.isDebugEnabled()) { - log.debug("the url is the same - no need to send it:" + destContent.getContentUrl()); + log.debug("the modified date is the same - no need to send it:" + destContent.getContentUrl()); } } else { - // We need to diff the property - out.missingContent(node.getNodeRef(), propEntry.getKey(), srcContent.getContentUrl()); + out.missingContent(node.getNodeRef(), propEntry.getKey(), TransferCommons.URLToPartName(srcContent.getContentUrl())); } } else { // We don't have the property on the destination node - out.missingContent(node.getNodeRef(), propEntry.getKey(), srcContent.getContentUrl()); + out.missingContent(node.getNodeRef(), propEntry.getKey(), TransferCommons.URLToPartName(srcContent.getContentUrl())); } } } @@ -171,7 +179,7 @@ public class RepoRequsiteManifestProcessorImpl extends AbstractManifestProcessor { ContentData content = (ContentData)value; // - out.missingContent(node.getNodeRef(), propEntry.getKey(), content.getContentUrl()); + out.missingContent(node.getNodeRef(), propEntry.getKey(), TransferCommons.URLToPartName(content.getContentUrl())); } } } diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceImpl.java b/source/java/org/alfresco/repo/transfer/TransferServiceImpl.java index f27f921d38..9c29dd465e 100644 --- a/source/java/org/alfresco/repo/transfer/TransferServiceImpl.java +++ b/source/java/org/alfresco/repo/transfer/TransferServiceImpl.java @@ -699,7 +699,8 @@ public class TransferServiceImpl implements TransferService */ if(deltaList != null) { - if(deltaList.getRequiredURLs().contains(d.getContentUrl())) + String partName = TransferCommons.URLToPartName(d.getContentUrl()); + if(deltaList.getRequiredParts().contains(partName)) { logger.debug("content is required :" + d.getContentUrl()); chunker.addContent(d); diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java b/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java index 8b3137e2c8..e856649b2a 100644 --- a/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java +++ b/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java @@ -19,6 +19,7 @@ package org.alfresco.repo.transfer; import java.util.ArrayList; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -62,6 +63,8 @@ import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.BaseAlfrescoSpringTest; import org.alfresco.util.GUID; import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.util.ResourceUtils; @@ -647,6 +650,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest endTransaction(); } + logger.debug("First transfer - create new node (no content yet)"); startNewTransaction(); try { @@ -676,10 +680,25 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest assertEquals("title is wrong", (String)nodeService.getProperty(destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE); assertEquals("type is wrong", nodeService.getType(contentNodeRef), nodeService.getType(destNodeRef)); + // Check the modified time of the destination node is the same as the source node. + Date destModifiedDate = (Date)nodeService.getProperty(destNodeRef, ContentModel.PROP_MODIFIED); + Date srcModifiedDate = (Date)nodeService.getProperty(contentNodeRef, ContentModel.PROP_MODIFIED); + + logger.debug("srcModifiedDate : " + srcModifiedDate + " destModifiedDate : " + destModifiedDate); + assertTrue("dest modified date is not correct", destModifiedDate.compareTo(srcModifiedDate)== 0); + + Date destCreatedDate = (Date)nodeService.getProperty(destNodeRef, ContentModel.PROP_CREATED); + Date srcCreatedDate = (Date)nodeService.getProperty(contentNodeRef, ContentModel.PROP_CREATED); + + logger.debug("srcCreatedDate : " + srcCreatedDate + " destCreatedDate : " + destCreatedDate); + assertTrue("dest created date is not correct", destCreatedDate.compareTo(srcCreatedDate)== 0); + + + // Check injected transferred aspect. assertNotNull("transferredAspect", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID)); - // Now set up the next test which is to + // Now set up the next test which is to change the title nodeService.setProperty(contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE_UPDATED); } finally @@ -687,11 +706,12 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest endTransaction(); } + logger.debug("Second transfer - update title property (no content yet)"); startNewTransaction(); try { /** - * Transfer our node again - so this is an update + * Transfer our node again - so this is an update of the title property */ { TransferDefinition definition = new TransferDefinition(); @@ -715,6 +735,21 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest assertEquals("title is wrong", (String)nodeService.getProperty(destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE_UPDATED); assertEquals("type is wrong", nodeService.getType(contentNodeRef), nodeService.getType(destNodeRef)); + // Check the modified time of the destination node is the same as the source node. + Date destModifiedDate = (Date)nodeService.getProperty(destNodeRef, ContentModel.PROP_MODIFIED); + Date srcModifiedDate = (Date)nodeService.getProperty(contentNodeRef, ContentModel.PROP_MODIFIED); + + logger.debug("srcModifiedDate : " + srcModifiedDate + " destModifiedDate : " + destModifiedDate); + + // BUGBUG - MER 14/07/2010 - can't set modified date + // assertTrue("after update, modified date is not correct", destModifiedDate.compareTo(srcModifiedDate) == 0); + + Date destCreatedDate = (Date)nodeService.getProperty(destNodeRef, ContentModel.PROP_CREATED); + Date srcCreatedDate = (Date)nodeService.getProperty(contentNodeRef, ContentModel.PROP_CREATED); + + logger.debug("srcCreatedDate : " + srcCreatedDate + " destCreatedDate : " + destCreatedDate); + assertTrue("after update, created date is not correct", destCreatedDate.compareTo(srcCreatedDate)== 0); + // Check injected transferred aspect. assertNotNull("transferredAspect", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID)); @@ -724,6 +759,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest endTransaction(); } + logger.debug("Transfer again - this is an update"); startNewTransaction(); try { @@ -744,9 +780,10 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest } /** - * Now transfer nothing - content items do not need to be transferred since its alrady on + * Now transfer nothing - content items do not need to be transferred since its already on * the destination. */ + logger.debug("Transfer again - with no new content"); startNewTransaction(); try { @@ -760,12 +797,29 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest { endTransaction(); } - + startNewTransaction(); + try + { + // Now validate that the target node still exists and in particular that the old content is still there + assertFalse("unit test stuffed up - comparing with self", destNodeRef.equals(transferMe.getNodeRef())); + assertTrue("dest node ref does not exist", nodeService.exists(destNodeRef)); + assertEquals("title is wrong", (String)nodeService.getProperty(destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE_UPDATED); + assertEquals("type is wrong", nodeService.getType(contentNodeRef), nodeService.getType(destNodeRef)); + + // Check injected transferred aspect. + assertNotNull("transferredAspect", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID)); + + } + finally + { + endTransaction(); + } /** * Negative test transfer nothing */ + logger.debug("Transfer again - with no content - should throw exception"); try { TransferDefinition definition = new TransferDefinition(); diff --git a/source/java/org/alfresco/repo/transfer/requisite/DeltaListRequsiteProcessor.java b/source/java/org/alfresco/repo/transfer/requisite/DeltaListRequsiteProcessor.java index 8c5c7f0c4a..1ad4ed2f0f 100644 --- a/source/java/org/alfresco/repo/transfer/requisite/DeltaListRequsiteProcessor.java +++ b/source/java/org/alfresco/repo/transfer/requisite/DeltaListRequsiteProcessor.java @@ -38,7 +38,7 @@ public class DeltaListRequsiteProcessor implements TransferRequsiteProcessor public void missingContent(NodeRef node, QName qname, String name) { - deltaList.getRequiredURLs().add(name); + deltaList.getRequiredParts().add(name); } public void startTransferRequsite() diff --git a/source/java/org/alfresco/repo/transfer/requisite/RequsiteModel.java b/source/java/org/alfresco/repo/transfer/requisite/RequsiteModel.java index 059ea2acda..30e8d17f74 100644 --- a/source/java/org/alfresco/repo/transfer/requisite/RequsiteModel.java +++ b/source/java/org/alfresco/repo/transfer/requisite/RequsiteModel.java @@ -25,6 +25,8 @@ import org.alfresco.repo.transfer.TransferModel; */ public interface RequsiteModel extends TransferModel { + static final String REQUSITE_MODEL_1_0_URI = "http://www.alfresco.org/model/requsite/1.0"; + static final String LOCALNAME_TRANSFER_REQUSITE = "transferRequsite"; static final String LOCALNAME_ELEMENT_NODES = "nodes"; @@ -37,5 +39,5 @@ public interface RequsiteModel extends TransferModel // Manifest file prefix - static final String REQUSITE_PREFIX = "xferreq"; + static final String REQUSITE_PREFIX = "xferr"; } diff --git a/source/java/org/alfresco/repo/transfer/requisite/XMLTransferRequsiteReader.java b/source/java/org/alfresco/repo/transfer/requisite/XMLTransferRequsiteReader.java index a8f4f8dc34..2c017db635 100644 --- a/source/java/org/alfresco/repo/transfer/requisite/XMLTransferRequsiteReader.java +++ b/source/java/org/alfresco/repo/transfer/requisite/XMLTransferRequsiteReader.java @@ -51,7 +51,7 @@ public class XMLTransferRequsiteReader extends DefaultHandler implements Content */ LinkedList> namespaces = new LinkedList>(); - final String TRANSFER_URI = RequsiteModel.TRANSFER_MODEL_1_0_URI; + final String REQUSITE_URI = RequsiteModel.REQUSITE_MODEL_1_0_URI; final String XMLNS_URI = "http://www.w3.org/XML/1998/namespace"; /* @@ -191,7 +191,7 @@ public class XMLTransferRequsiteReader extends DefaultHandler implements Content return; } - if(elementQName.getNamespaceURI().equals(TRANSFER_URI)); + if(elementQName.getNamespaceURI().equals(REQUSITE_URI)); { // This is one of the transfer manifest elements String elementName = elementQName.getLocalName(); diff --git a/source/java/org/alfresco/repo/transfer/requisite/XMLTransferRequsiteWriter.java b/source/java/org/alfresco/repo/transfer/requisite/XMLTransferRequsiteWriter.java index f3eb5b1e09..bdf01dc40a 100644 --- a/source/java/org/alfresco/repo/transfer/requisite/XMLTransferRequsiteWriter.java +++ b/source/java/org/alfresco/repo/transfer/requisite/XMLTransferRequsiteWriter.java @@ -90,7 +90,7 @@ public class XMLTransferRequsiteWriter implements TransferRequsiteWriter { this.writer.startDocument(); - this.writer.startPrefixMapping(PREFIX, TransferModel.TRANSFER_MODEL_1_0_URI); + this.writer.startPrefixMapping(PREFIX, RequsiteModel.TRANSFER_MODEL_1_0_URI); this.writer.startPrefixMapping("cm", NamespaceService.CONTENT_MODEL_1_0_URI); // Start Transfer Manifest // uri, name, prefix