Transfer service : more work on just sending content that is required.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21160 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Mark Rogers
2010-07-14 13:10:05 +00:00
parent 240e0d9f05
commit 9dde9b2ab5
9 changed files with 136 additions and 36 deletions

View File

@@ -31,18 +31,18 @@ import java.util.TreeSet;
public class DeltaList public class DeltaList
{ {
/** /**
* The set of requiredURLs * The set of requiredParts
*/ */
private TreeSet<String> requiredURLs = new TreeSet<String>(); private TreeSet<String> requiredParts = new TreeSet<String>();
/** /**
* get the list of URLs reqired by the manifest. * get the list of URLs reqired by the manifest.
* @return the list of required URLs * @return the list of required URLs
*/ */
public Set<String> getRequiredURLs() public Set<String> getRequiredParts()
{ {
return requiredURLs; return requiredParts;
} }
} }

View File

@@ -290,7 +290,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
Map<QName, Serializable> props = new HashMap<QName, Serializable>(node.getProperties()); Map<QName, Serializable> props = new HashMap<QName, Serializable>(node.getProperties());
// Split out the content properties and sanitise the others // Split out the content properties and sanitise the others
Map<QName, Serializable> contentProps = processProperties(null, props, true); Map<QName, Serializable> contentProps = processProperties(null, props, null);
// inject transferred property here // inject transferred property here
if(!contentProps.containsKey(TransferModel.PROP_REPOSITORY_ID)) if(!contentProps.containsKey(TransferModel.PROP_REPOSITORY_ID))
@@ -390,6 +390,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
// We need to process content properties separately. // We need to process content properties separately.
// First, create a shallow copy of the supplied property map... // First, create a shallow copy of the supplied property map...
Map<QName, Serializable> props = new HashMap<QName, Serializable>(node.getProperties()); Map<QName, Serializable> props = new HashMap<QName, Serializable>(node.getProperties());
Map<QName, Serializable> existingProps = nodeService.getProperties(nodeToUpdate);
// inject transferred property here // inject transferred property here
if(!props.containsKey(TransferModel.PROP_REPOSITORY_ID)) if(!props.containsKey(TransferModel.PROP_REPOSITORY_ID))
@@ -399,7 +400,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
} }
// Split out the content properties and sanitise the others // Split out the content properties and sanitise the others
Map<QName, Serializable> contentProps = processProperties(nodeToUpdate, props, false); Map<QName, Serializable> contentProps = processProperties(nodeToUpdate, props, existingProps);
// Update the non-content properties // Update the non-content properties
nodeService.setProperties(nodeToUpdate, props); nodeService.setProperties(nodeToUpdate, props);
@@ -448,17 +449,19 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
* @param nodeToUpdate * @param nodeToUpdate
* The noderef of the existing node in the local repo that is to be updated with these properties. May be * 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. * null, indicating that these properties are destined for a brand new local node.
* @param props * @param props the new properties
* @return A map containing the content properties from the supplied "props" map * @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<QName, Serializable> processProperties(NodeRef nodeToUpdate, Map<QName, Serializable> props, private Map<QName, Serializable> processProperties(NodeRef nodeToUpdate, Map<QName, Serializable> props,
boolean isNew) Map<QName, Serializable> existingProps)
{ {
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>(); Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>();
// ...and copy any supplied content properties into this new map... // ...and copy any supplied content properties into this new map...
for (Map.Entry<QName, Serializable> propEntry : props.entrySet()) for (Map.Entry<QName, Serializable> propEntry : props.entrySet())
{ {
Serializable value = propEntry.getValue(); Serializable value = propEntry.getValue();
QName key = propEntry.getKey();
if (log.isDebugEnabled()) if (log.isDebugEnabled())
{ {
if (value == null) if (value == null)
@@ -468,8 +471,43 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
} }
if ((value != null) && ContentData.class.isAssignableFrom(value.getClass())) if ((value != null) && ContentData.class.isAssignableFrom(value.getClass()))
{ {
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()); 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());
}
}
} }
// Now we can remove the content properties from amongst the other kinds // Now we can remove the content properties from amongst the other kinds
@@ -480,13 +518,10 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
props.remove(contentPropertyName); props.remove(contentPropertyName);
} }
if (!isNew) if (existingProps != null)
{ {
// Finally, overlay the repo-specific properties from the existing // Finally, overlay the repo-specific properties from the existing
// node (if there is one) // node (if there is one)
Map<QName, Serializable> existingProps = (nodeToUpdate == null) ? new HashMap<QName, Serializable>()
: nodeService.getProperties(nodeToUpdate);
for (QName localProperty : getLocalProperties()) for (QName localProperty : getLocalProperties())
{ {
Serializable existingValue = existingProps.get(localProperty); Serializable existingValue = existingProps.get(localProperty);
@@ -515,7 +550,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
{ {
ContentData contentData = (ContentData) contentEntry.getValue(); ContentData contentData = (ContentData) contentEntry.getValue();
String contentUrl = contentData.getContentUrl(); String contentUrl = contentData.getContentUrl();
String fileName = contentUrl.substring(contentUrl.lastIndexOf('/') + 1); String fileName = TransferCommons.URLToPartName(contentUrl);
File stagedFile = new File(stagingDir, fileName); File stagedFile = new File(stagingDir, fileName);
if (!stagedFile.exists()) if (!stagedFile.exists())
{ {

View File

@@ -22,6 +22,7 @@ package org.alfresco.repo.transfer;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -105,9 +106,9 @@ public class RepoRequsiteManifestProcessorImpl extends AbstractManifestProcessor
*/ */
NodeRef destinationNode = resolvedNodes.resolvedChild; NodeRef destinationNode = resolvedNodes.resolvedChild;
// Serializable yy = node.getProperties().get(ContentModel.PROP_MODIFIED);
Map<QName, Serializable> destProps = nodeService.getProperties(destinationNode); Map<QName, Serializable> destProps = nodeService.getProperties(destinationNode);
// Serializable xx = destProps.get(ContentModel.PROP_MODIFIED);
for (Map.Entry<QName, Serializable> propEntry : node.getProperties().entrySet()) for (Map.Entry<QName, Serializable> propEntry : node.getProperties().entrySet())
{ {
@@ -128,26 +129,33 @@ public class RepoRequsiteManifestProcessorImpl extends AbstractManifestProcessor
ContentData destContent = (ContentData)destProps.get(propEntry.getKey()); 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( Serializable srcModified = node.getProperties().get(ContentModel.PROP_MODIFIED);
TransferCommons.URLToPartName(srcContent.getContentUrl()))) 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()) 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 else
{ {
// We need to diff the property out.missingContent(node.getNodeRef(), propEntry.getKey(), TransferCommons.URLToPartName(srcContent.getContentUrl()));
out.missingContent(node.getNodeRef(), propEntry.getKey(), srcContent.getContentUrl());
} }
} }
else else
{ {
// We don't have the property on the destination node // 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; ContentData content = (ContentData)value;
// //
out.missingContent(node.getNodeRef(), propEntry.getKey(), content.getContentUrl()); out.missingContent(node.getNodeRef(), propEntry.getKey(), TransferCommons.URLToPartName(content.getContentUrl()));
} }
} }
} }

View File

@@ -699,7 +699,8 @@ public class TransferServiceImpl implements TransferService
*/ */
if(deltaList != null) 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()); logger.debug("content is required :" + d.getContentUrl());
chunker.addContent(d); chunker.addContent(d);

View File

@@ -19,6 +19,7 @@
package org.alfresco.repo.transfer; package org.alfresco.repo.transfer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -62,6 +63,8 @@ import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.BaseAlfrescoSpringTest; import org.alfresco.util.BaseAlfrescoSpringTest;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
import org.alfresco.util.Pair; 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.TransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
@@ -647,6 +650,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
endTransaction(); endTransaction();
} }
logger.debug("First transfer - create new node (no content yet)");
startNewTransaction(); startNewTransaction();
try try
{ {
@@ -676,10 +680,25 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
assertEquals("title is wrong", (String)nodeService.getProperty(destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE); assertEquals("title is wrong", (String)nodeService.getProperty(destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE);
assertEquals("type is wrong", nodeService.getType(contentNodeRef), nodeService.getType(destNodeRef)); 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. // Check injected transferred aspect.
assertNotNull("transferredAspect", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID)); 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); nodeService.setProperty(contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE_UPDATED);
} }
finally finally
@@ -687,11 +706,12 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
endTransaction(); endTransaction();
} }
logger.debug("Second transfer - update title property (no content yet)");
startNewTransaction(); startNewTransaction();
try 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(); 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("title is wrong", (String)nodeService.getProperty(destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE_UPDATED);
assertEquals("type is wrong", nodeService.getType(contentNodeRef), nodeService.getType(destNodeRef)); 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. // Check injected transferred aspect.
assertNotNull("transferredAspect", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID)); assertNotNull("transferredAspect", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID));
@@ -724,6 +759,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
endTransaction(); endTransaction();
} }
logger.debug("Transfer again - this is an update");
startNewTransaction(); startNewTransaction();
try 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. * the destination.
*/ */
logger.debug("Transfer again - with no new content");
startNewTransaction(); startNewTransaction();
try try
{ {
@@ -761,11 +798,28 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
endTransaction(); 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 * Negative test transfer nothing
*/ */
logger.debug("Transfer again - with no content - should throw exception");
try try
{ {
TransferDefinition definition = new TransferDefinition(); TransferDefinition definition = new TransferDefinition();

View File

@@ -38,7 +38,7 @@ public class DeltaListRequsiteProcessor implements TransferRequsiteProcessor
public void missingContent(NodeRef node, QName qname, String name) public void missingContent(NodeRef node, QName qname, String name)
{ {
deltaList.getRequiredURLs().add(name); deltaList.getRequiredParts().add(name);
} }
public void startTransferRequsite() public void startTransferRequsite()

View File

@@ -25,6 +25,8 @@ import org.alfresco.repo.transfer.TransferModel;
*/ */
public interface RequsiteModel extends 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_TRANSFER_REQUSITE = "transferRequsite";
static final String LOCALNAME_ELEMENT_NODES = "nodes"; static final String LOCALNAME_ELEMENT_NODES = "nodes";
@@ -37,5 +39,5 @@ public interface RequsiteModel extends TransferModel
// Manifest file prefix // Manifest file prefix
static final String REQUSITE_PREFIX = "xferreq"; static final String REQUSITE_PREFIX = "xferr";
} }

View File

@@ -51,7 +51,7 @@ public class XMLTransferRequsiteReader extends DefaultHandler implements Content
*/ */
LinkedList<HashMap<String, String>> namespaces = new LinkedList<HashMap<String, String>>(); LinkedList<HashMap<String, String>> namespaces = new LinkedList<HashMap<String, String>>();
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"; final String XMLNS_URI = "http://www.w3.org/XML/1998/namespace";
/* /*
@@ -191,7 +191,7 @@ public class XMLTransferRequsiteReader extends DefaultHandler implements Content
return; return;
} }
if(elementQName.getNamespaceURI().equals(TRANSFER_URI)); if(elementQName.getNamespaceURI().equals(REQUSITE_URI));
{ {
// This is one of the transfer manifest elements // This is one of the transfer manifest elements
String elementName = elementQName.getLocalName(); String elementName = elementQName.getLocalName();

View File

@@ -90,7 +90,7 @@ public class XMLTransferRequsiteWriter implements TransferRequsiteWriter
{ {
this.writer.startDocument(); 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); this.writer.startPrefixMapping("cm", NamespaceService.CONTENT_MODEL_1_0_URI);
// Start Transfer Manifest // uri, name, prefix // Start Transfer Manifest // uri, name, prefix