transfer service : work in progress.

implementation of transfer requsite.
    API change isComplete to isSync

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21130 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Mark Rogers
2010-07-13 14:04:23 +00:00
parent eece9fef5e
commit e468ab3a4b
26 changed files with 1294 additions and 72 deletions

View File

@@ -151,7 +151,7 @@ public class ReplicationActionExecutor extends ActionExecuterAbstractBase {
TransferDefinition transferDefinition =
new TransferDefinition();
transferDefinition.setNodes(toTransfer);
transferDefinition.setComplete(true);
transferDefinition.setSync(true);
return transferDefinition;
}

View File

@@ -632,7 +632,7 @@ public class ReplicationServiceIntegrationTest extends BaseAlfrescoSpringTest
nodes.add(content1_1);
TransferDefinition td = replicationActionExecutor.buildTransferDefinition(rd, nodes);
assertEquals(true, td.isComplete());
assertEquals(true, td.isSync());
assertEquals(2, td.getNodes().size());
assertEquals(true, td.getNodes().contains(folder1));
assertEquals(true, td.getNodes().contains(content1_1));

View File

@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.List;
import org.alfresco.repo.transfer.manifest.TransferManifestProcessor;
import org.alfresco.repo.transfer.requisite.TransferRequsiteWriter;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeService;
@@ -102,4 +103,20 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac
this.nodeResolverFactory = nodeResolverFactory;
}
/**
*
*/
public TransferManifestProcessor getRequsiteProcessor(
TransferReceiver receiver, String transferId, TransferRequsiteWriter out)
{
RepoRequsiteManifestProcessorImpl processor = new RepoRequsiteManifestProcessorImpl(receiver, transferId, out);
CorrespondingNodeResolver nodeResolver = nodeResolverFactory.getResolver();
processor.setNodeResolver(nodeResolver);
processor.setNodeService(nodeService);
return processor;
}
}

View File

@@ -18,12 +18,31 @@
*/
package org.alfresco.repo.transfer;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**
* Details back from the manifest to say which nodes the remote server already has.
* Details back from reading the manifest to say what is required to fulfill the manifest.
*
* @author Mark Rogers
*/
public class DeltaList
{
/**
* The set of requiredURLs
*/
private TreeSet<String> requiredURLs = new TreeSet<String>();
/**
* get the list of URLs reqired by the manifest.
* @return the list of required URLs
*/
public Set<String> getRequiredURLs()
{
return requiredURLs;
}
}

View File

@@ -19,7 +19,13 @@
package org.alfresco.repo.transfer;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
@@ -254,7 +260,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter
}
}
public DeltaList sendManifest(Transfer transfer, File manifest) throws TransferException
public void sendManifest(Transfer transfer, File manifest, OutputStream result) throws TransferException
{
TransferTarget target = transfer.getTransferTarget();
PostMethod postSnapshotRequest = new PostMethod();
@@ -288,7 +294,23 @@ public class HttpClientTransmitterImpl implements TransferTransmitter
int responseStatus = httpClient.executeMethod(hostConfig, postSnapshotRequest, httpState);
checkResponseStatus("sendManifest", responseStatus, postSnapshotRequest);
return null;
InputStream is = postSnapshotRequest.getResponseBodyAsStream();
InputStreamReader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(result));
String s = br.readLine();
while(s != null)
{
bw.write(s);
s = br.readLine();
}
bw.close();
return;
}
catch (RuntimeException e)
{
@@ -453,7 +475,6 @@ public class HttpClientTransmitterImpl implements TransferTransmitter
int index = 0;
for(ContentData content : data)
{
// TODO Encapsulate the URL to FileName algorithm
String contentUrl = content.getContentUrl();
String fileName = TransferCommons.URLToPartName(contentUrl);
log.debug("content partName: " + fileName);

View File

@@ -22,13 +22,29 @@ package org.alfresco.repo.transfer;
import java.util.List;
import org.alfresco.repo.transfer.manifest.TransferManifestProcessor;
import org.alfresco.repo.transfer.requisite.TransferRequsiteWriter;
import org.alfresco.service.cmr.transfer.TransferReceiver;
/**
* @author brian
*
* This is a factory class for the processors of the transfer manifest file.
*/
public interface ManifestProcessorFactory
{
/**
* The requisite processor
* @param receiver
* @param transferId
* @return the requisite processor
*/
TransferManifestProcessor getRequsiteProcessor(TransferReceiver receiver, String transferId, TransferRequsiteWriter out);
/**
* The commit processors
* @param receiver
* @param transferId
* @return the requsite processor
*/
List<TransferManifestProcessor> getCommitProcessors(TransferReceiver receiver, String transferId);
}

View File

@@ -0,0 +1,213 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.transfer;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.transfer.manifest.TransferManifestDeletedNode;
import org.alfresco.repo.transfer.manifest.TransferManifestHeader;
import org.alfresco.repo.transfer.manifest.TransferManifestNodeHelper;
import org.alfresco.repo.transfer.manifest.TransferManifestNormalNode;
import org.alfresco.repo.transfer.requisite.TransferRequsiteWriter;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.transfer.TransferReceiver;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXParseException;
/**
* @author mrogers
*
* The requsite manifest processor performs a parse of the manifest file to determine which
* resources are required.
*
*/
public class RepoRequsiteManifestProcessorImpl extends AbstractManifestProcessorBase
{
private NodeService nodeService;
private CorrespondingNodeResolver nodeResolver;
private TransferRequsiteWriter out;
private static final Log log = LogFactory.getLog(RepoRequsiteManifestProcessorImpl.class);
/**
* @param receiver
* @param transferId
*/
public RepoRequsiteManifestProcessorImpl(TransferReceiver receiver, String transferId, TransferRequsiteWriter out)
{
super(receiver, transferId);
this.out = out;
}
protected void endManifest()
{
log.debug("End Requsite");
out.endTransferRequsite();
}
protected void processNode(TransferManifestDeletedNode node)
{
//NOOP
}
protected void processNode(TransferManifestNormalNode node)
{
if (log.isDebugEnabled())
{
log.debug("Processing node with incoming noderef of " + node.getNodeRef());
}
logProgress("Processing incoming node: " + node.getNodeRef() + " -- Source path = " + node.getParentPath() + "/" + node.getPrimaryParentAssoc().getQName());
ChildAssociationRef primaryParentAssoc = node.getPrimaryParentAssoc();
CorrespondingNodeResolver.ResolvedParentChildPair resolvedNodes = nodeResolver.resolveCorrespondingNode(node
.getNodeRef(), primaryParentAssoc, node.getParentPath());
// Does a corresponding node exist in this repo?
if (resolvedNodes.resolvedChild != null)
{
/**
* there is a corresponding node so we need to check whether we already
* have the content item
*/
NodeRef destinationNode = resolvedNodes.resolvedChild;
// Serializable yy = node.getProperties().get(ContentModel.PROP_MODIFIED);
Map<QName, Serializable> destProps = nodeService.getProperties(destinationNode);
// Serializable xx = destProps.get(ContentModel.PROP_MODIFIED);
for (Map.Entry<QName, Serializable> propEntry : node.getProperties().entrySet())
{
Serializable value = propEntry.getValue();
if (log.isDebugEnabled())
{
if (value == null)
{
log.debug("Received a null value for property " + propEntry.getKey());
}
}
if ((value != null) && ContentData.class.isAssignableFrom(value.getClass()))
{
ContentData srcContent = (ContentData)value;
Serializable destSer = destProps.get(propEntry.getKey());
if(destSer != null && ContentData.class.isAssignableFrom(destSer.getClass()))
{
ContentData destContent = (ContentData)destProps.get(propEntry.getKey());
/**
* If the URLs are the same then the content is already on the server
*/
if(TransferCommons.URLToPartName(destContent.getContentUrl()).equalsIgnoreCase(
TransferCommons.URLToPartName(srcContent.getContentUrl())))
{
if(log.isDebugEnabled())
{
log.debug("the url 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());
}
}
else
{
// We don't have the property on the destination node
out.missingContent(node.getNodeRef(), propEntry.getKey(), srcContent.getContentUrl());
}
}
}
}
else
{
/**
* there is no corresponding node so all content properties are "missing."
*/
for (Map.Entry<QName, Serializable> propEntry : node.getProperties().entrySet())
{
Serializable value = propEntry.getValue();
if (log.isDebugEnabled())
{
if (value == null)
{
log.debug("Received a null value for property " + propEntry.getKey());
}
}
if ((value != null) && ContentData.class.isAssignableFrom(value.getClass()))
{
ContentData content = (ContentData)value;
//
out.missingContent(node.getNodeRef(), propEntry.getKey(), content.getContentUrl());
}
}
}
}
protected void processHeader(TransferManifestHeader header)
{
// T.B.D
}
/*
* (non-Javadoc)
*
* @see org.alfresco.repo.transfer.manifest.TransferManifestProcessor#startTransferManifest()
*/
protected void startManifest()
{
log.debug("Start Requsite");
out.startTransferRequsite();
}
/**
* @param nodeService
* the nodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param nodeResolver
* the nodeResolver to set
*/
public void setNodeResolver(CorrespondingNodeResolver nodeResolver)
{
this.nodeResolver = nodeResolver;
}
}

View File

@@ -56,9 +56,9 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor
private static final Log log = LogFactory.getLog(RepoTertiaryManifestProcessorImpl.class);
/**
* Is this a "complete" transfer. If not then does nothing.
* Is this a "sync" transfer. If not then does nothing.
*/
boolean isComplete = false;
boolean isSync = false;
/**
* @param receiver
@@ -84,7 +84,7 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor
NodeRef nodeRef = node.getNodeRef();
log.debug("processNode " + nodeRef);
if(isComplete)
if(isSync)
{
List<ChildAssociationRef> expectedChildren = node.getChildAssocs();
@@ -144,8 +144,8 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor
protected void processHeader(TransferManifestHeader header)
{
isComplete = header.isComplete();
log.debug("isComplete :" + isComplete);
isSync = header.isSync();
log.debug("isSync :" + isSync);
}
/*

View File

@@ -23,7 +23,10 @@ import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
@@ -45,6 +48,7 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.transfer.manifest.TransferManifestProcessor;
import org.alfresco.repo.transfer.manifest.XMLTransferManifestReader;
import org.alfresco.repo.transfer.requisite.XMLTransferRequsiteWriter;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -127,7 +131,8 @@ public class RepoTransferReceiverImpl implements TransferReceiver
private static final String MSG_ERROR_WHILE_STAGING_CONTENT = "transfer_service.receiver.error_staging_content";
private static final String MSG_NO_SNAPSHOT_RECEIVED = "transfer_service.receiver.no_snapshot_received";
private static final String MSG_ERROR_WHILE_COMMITTING_TRANSFER = "transfer_service.receiver.error_committing_transfer";
private static final String MSG_ERROR_WHILE_GENERATING_REQUISITE = "transfer_service.receiver.error_generating_requsite";
private static final String LOCK_FILE_NAME = ".lock";
private static final QName LOCK_QNAME = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, LOCK_FILE_NAME);
private static final String SNAPSHOT_FILE_NAME = "snapshot.xml";
@@ -846,4 +851,55 @@ public class RepoTransferReceiverImpl implements TransferReceiver
return this.ruleService;
}
/**
* Generate the requsite
*/
public void generateRequsite(String transferId, OutputStream out) throws TransferException
{
log.debug("Generate Requsite for transfer:" + transferId);
try
{
File snapshotFile = getSnapshotFile(transferId);
if (snapshotFile.exists())
{
log.debug("snapshot does exist");
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser parser = saxParserFactory.newSAXParser();
OutputStreamWriter dest = new OutputStreamWriter(out, "UTF-8");
XMLTransferRequsiteWriter writer = new XMLTransferRequsiteWriter(dest);
TransferManifestProcessor processor = manifestProcessorFactory.getRequsiteProcessor(
RepoTransferReceiverImpl.this,
transferId,
writer);
XMLTransferManifestReader reader = new XMLTransferManifestReader(processor);
/**
* Now run the parser
*/
parser.parse(snapshotFile, reader);
/**
* And flush the destination in case any content remains in the writer.
*/
dest.flush();
}
log.debug("Generate Requsite done transfer:" + transferId);
}
catch (Exception ex)
{
if (TransferException.class.isAssignableFrom(ex.getClass()))
{
throw (TransferException) ex;
}
else
{
throw new TransferException(MSG_ERROR_WHILE_GENERATING_REQUISITE, ex);
}
}
}
}

View File

@@ -20,6 +20,7 @@ package org.alfresco.repo.transfer;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
@@ -57,6 +58,8 @@ import org.alfresco.repo.transfer.manifest.TransferManifestWriter;
import org.alfresco.repo.transfer.manifest.XMLTransferManifestReader;
import org.alfresco.repo.transfer.manifest.XMLTransferManifestWriter;
import org.alfresco.repo.transfer.report.TransferReporter;
import org.alfresco.repo.transfer.requisite.DeltaListRequsiteProcessor;
import org.alfresco.repo.transfer.requisite.XMLTransferRequsiteReader;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -530,6 +533,8 @@ public class TransferServiceImpl implements TransferService
eventProcessor.addObserver(reportCallback);
File snapshotFile = null;
File reqFile = null;
TransferTarget target = null;
try
{
@@ -555,6 +560,8 @@ public class TransferServiceImpl implements TransferService
// where to put snapshot ?
File tempDir = TempFileProvider.getLongLifeTempDir("transfer");
snapshotFile = TempFileProvider.createTempFile(prefix, suffix, tempDir);
reqFile = TempFileProvider.createTempFile("TRX-REQ", suffix, tempDir);
FileOutputStream reqOutput = new FileOutputStream(reqFile);
FileWriter snapshotWriter = new FileWriter(snapshotFile);
@@ -565,7 +572,7 @@ public class TransferServiceImpl implements TransferService
header.setRepositoryId(descriptor.getId());
header.setCreatedDate(new Date());
header.setNodeCount(nodes.size());
header.setComplete(definition.isComplete());
header.setSync(definition.isSync());
formatter.startTransferManifest(snapshotWriter);
formatter.writeTransferManifestHeader(header);
for(NodeRef nodeRef : nodes)
@@ -612,12 +619,41 @@ public class TransferServiceImpl implements TransferService
checkCancel(transferId);
/**
* send Manifest
* send Manifest, get the requsite back.
*/
eventProcessor.sendSnapshot(1,1);
transmitter.sendManifest(transfer, snapshotFile);
transmitter.sendManifest(transfer, snapshotFile, reqOutput);
if(logger.isDebugEnabled())
{
logger.debug("requsite file written to local filesystem");
try
{
outputFile(reqFile);
}
catch (IOException error)
{
// This is debug code - so an exception thrown while debugging
logger.debug("error while outputting snapshotFile");
error.printStackTrace();
}
}
logger.debug("manifest sent");
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser parser;
parser = saxParserFactory.newSAXParser();
/**
* Parse the requsite file to generate the delta list
*/
DeltaListRequsiteProcessor reqProcessor = new DeltaListRequsiteProcessor();
XMLTransferRequsiteReader reqReader = new XMLTransferRequsiteReader(reqProcessor);
parser.parse(reqFile, reqReader);
final DeltaList deltaList = reqProcessor.getDeltaList();
/**
* Parse the manifest file and transfer chunks over
@@ -657,7 +693,23 @@ public class TransferServiceImpl implements TransferService
{
checkCancel(transfer.getTransferId());
logger.debug("add content to chunker");
chunker.addContent(d);
/**
* Check with the deltaList whether we need to send the content item
*/
if(deltaList != null)
{
if(deltaList.getRequiredURLs().contains(d.getContentUrl()))
{
logger.debug("content is required :" + d.getContentUrl());
chunker.addContent(d);
}
}
else
{
// No delta list - so send all content items
chunker.addContent(d);
}
}
}
@@ -672,13 +724,11 @@ public class TransferServiceImpl implements TransferService
/**
* Step 3: wire up the manifest reader to a manifest processor
*/
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser parser;
parser = saxParserFactory.newSAXParser();
XMLTransferManifestReader reader = new XMLTransferManifestReader(processor);
/**
* Step 4: start the magic Give the manifest file to the manifest reader
* Step 4: start the magic - Give the manifest file to the manifest reader
*/
parser.parse(snapshotFile, reader);
chunker.flush();
@@ -821,6 +871,13 @@ public class TransferServiceImpl implements TransferService
snapshotFile.delete();
}
logger.debug("snapshot file deleted");
if(reqFile != null)
{
reqFile.delete();
}
logger.debug("req file deleted");
}
} // end of transferImpl

View File

@@ -724,6 +724,45 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
endTransaction();
}
startNewTransaction();
try
{
/**
* Transfer our node again - so this is an update
*/
{
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
}
}
finally
{
endTransaction();
}
/**
* Now transfer nothing - content items do not need to be transferred since its alrady on
* the destination.
*/
startNewTransaction();
try
{
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
}
finally
{
endTransaction();
}
/**
* Negative test transfer nothing
*/
@@ -1864,20 +1903,30 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
* Tree of nodes
*
* A1
* | | |
* A2 A3 (Content) B6
* | | |
* A2 A3 (Content Node) B6
* |
* A4 A5 B7 (content)
* A4 A5 B7 (Content Node)
*
* Test steps -
* 1 add A1
* transfer(sync)
* 2 add A2, A3, A4, A5
* 3 remove A2 (A4 and A5 should cascade delete on source)
* transfer(sync)
* 3 remove A2
* transfer(sync) A4 and A5 should cascade delete on source
* 4 remove A3
* 5 add back A3
* transfer(sync)
* 5 add back A3 - new node ref
* transfer(sync)
* 6 add A2, A4, A5
* 7 add B6 and B7 directly to target (so not transferred) transfer again.
*
* transfer(sync)
* 7 add B6 and B7 directly to target (so not transferred)
* transfer again, B6 and B7 should not be removed.
* 8 remove A2 (A2, A5, B7) should go. TODO is it correct that B7 goes?
* transfer
* restore node A2
* transfer
*/
public void testTransferSyncNodes() throws Exception
{
@@ -2011,7 +2060,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(A1NodeRef);
definition.setNodes(nodes);
definition.setComplete(true);
definition.setSync(true);
transferService.transfer(targetName, definition);
}
}
@@ -2056,7 +2105,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
nodes.add(A4NodeRef);
nodes.add(A5NodeRef);
definition.setNodes(nodes);
definition.setComplete(true);
definition.setSync(true);
transferService.transfer(targetName, definition);
}
}
@@ -2113,7 +2162,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
//nodes.add(A4NodeRef);
//nodes.add(A5NodeRef);
definition.setNodes(nodes);
definition.setComplete(true);
definition.setSync(true);
transferService.transfer(targetName, definition);
}
}
@@ -2169,7 +2218,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
//nodes.add(A4NodeRef);
//nodes.add(A5NodeRef);
definition.setNodes(nodes);
definition.setComplete(true);
definition.setSync(true);
transferService.transfer(targetName, definition);
}
}
@@ -2232,7 +2281,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
//nodes.add(A4NodeRef);
//nodes.add(A5NodeRef);
definition.setNodes(nodes);
definition.setComplete(true);
definition.setSync(true);
transferService.transfer(targetName, definition);
}
}
@@ -2319,7 +2368,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
nodes.add(A4NodeRef);
nodes.add(A5NodeRef);
definition.setNodes(nodes);
definition.setComplete(true);
definition.setSync(true);
transferService.transfer(targetName, definition);
}
}
@@ -2397,7 +2446,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
nodes.add(A4NodeRef);
nodes.add(A5NodeRef);
definition.setNodes(nodes);
definition.setComplete(true);
definition.setSync(true);
transferService.transfer(targetName, definition);
}
}
@@ -2425,6 +2474,123 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
{
endTransaction();
}
//TODO BUGBUG - test fails
// /** Step 8
// * remove A2 (A2, A5, B7) should go. TODO is it correct that B7 goes?
// * transfer
// * restore node A2
// * transfer
// */
// /**
// * Step 3 - remove folder node A2
// */
// startNewTransaction();
// try
// {
// nodeService.deleteNode(A2NodeRef);
// }
// finally
// {
// endTransaction();
// }
//
// startNewTransaction();
// try
// {
// /**
// * Transfer Node A 1-5
// */
// {
// TransferDefinition definition = new TransferDefinition();
// Set<NodeRef>nodes = new HashSet<NodeRef>();
// nodes.add(A1NodeRef);
// //nodes.add(A2NodeRef);
// nodes.add(A3NodeRef);
// //nodes.add(A4NodeRef);
// //nodes.add(A5NodeRef);
// definition.setNodes(nodes);
// definition.setSync(true);
// transferService.transfer(targetName, definition);
// }
// }
// finally
// {
// endTransaction();
// }
//
// startNewTransaction();
// try
// {
// // Now validate that the target node exists and has similar properties to the source
// destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef);
// assertFalse("unit test stuffed up - comparing with self", destNodeRef.equals(transferMe.getNodeRef()));
// assertTrue("dest node ref A1 does not exist", nodeService.exists(testNodeFactory.getMappedNodeRef(A1NodeRef)));
// assertFalse("dest node ref A2 has not been deleted", nodeService.exists(testNodeFactory.getMappedNodeRef(A2NodeRef)));
// assertTrue("dest node ref A3 does not exist", nodeService.exists(testNodeFactory.getMappedNodeRef(A3NodeRef)));
// assertFalse("dest node ref A4 has not been deleted", nodeService.exists(testNodeFactory.getMappedNodeRef(A4NodeRef)));
// assertFalse("dest node ref A5 has not been deleted", nodeService.exists(testNodeFactory.getMappedNodeRef(A5NodeRef)));
// // assertFalse("dest node B6 has not been deleted", nodeService.exists(B6NodeRef));
// // assertTrue("dest node B7 does not exist", nodeService.exists(B7NodeRef));
// }
// finally
// {
// endTransaction();
// }
//
// startNewTransaction();
// try
// {
// NodeRef archivedNode = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, A2NodeRef.getId());
// nodeService.restoreNode(archivedNode, A1NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A2"));
// }
// finally
// {
// endTransaction();
// }
// startNewTransaction();
// try
// {
// /**
// * Transfer Node A 1-5, B6 and B7 should remain untouched.
// */
// {
// TransferDefinition definition = new TransferDefinition();
// Set<NodeRef>nodes = new HashSet<NodeRef>();
// nodes.add(A1NodeRef);
// nodes.add(A2NodeRef);
// nodes.add(A3NodeRef);
// nodes.add(A4NodeRef);
// nodes.add(A5NodeRef);
// definition.setNodes(nodes);
// definition.setSync(true);
// transferService.transfer(targetName, definition);
// }
// }
// finally
// {
// endTransaction();
// }
//
// startNewTransaction();
// try
// {
// // Now validate that the target node exists and has similar properties to the source
// destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef);
// assertFalse("unit test stuffed up - comparing with self", destNodeRef.equals(transferMe.getNodeRef()));
// assertTrue("dest node ref A1 does not exist", nodeService.exists(testNodeFactory.getMappedNodeRef(A1NodeRef)));
// assertTrue("dest node ref A2 does not exist", nodeService.exists(testNodeFactory.getMappedNodeRef(A2NodeRef)));
// assertTrue("dest node ref A3 does not exist", nodeService.exists(testNodeFactory.getMappedNodeRef(A3NodeRef)));
// assertTrue("dest node ref A4 does not exist", nodeService.exists(testNodeFactory.getMappedNodeRef(A4NodeRef)));
// assertTrue("dest node ref A5 does not exist", nodeService.exists(testNodeFactory.getMappedNodeRef(A5NodeRef)));
// // assertFalse("dest node B6 has not been deleted", nodeService.exists(B6NodeRef));
// // assertTrue("dest node B7 does not exist", nodeService.exists(B7NodeRef));
// }
// finally
// {
// endTransaction();
// }
}
private TransferTarget createTransferTarget(String name)
@@ -2433,7 +2599,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
String description = "description";
String endpointProtocol = "http";
String endpointHost = "MARKR02";
int endpointPort = 6080;
int endpointPort = 7080;
String endpointPath = "/alfresco/service/api/transfer";
String username = "admin";
char[] password = "admin".toCharArray();
@@ -2444,5 +2610,4 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest
TransferTarget target = transferService.createAndSaveTransferTarget(name, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
return target;
}
}

View File

@@ -20,6 +20,7 @@
package org.alfresco.repo.transfer;
import java.io.File;
import java.io.OutputStream;
import java.util.Set;
import org.alfresco.service.cmr.repository.ContentData;
@@ -53,10 +54,12 @@ public interface TransferTransmitter
/**
* @param manifest, the transfer manifest file
* @param transfer the transfer object returned by an earlier call to begin
* @return the delta list.
* @param results - where to write the results, probably a temporary file the output steam should be
* open and will be closed before the method returns.
* @return the transfer requisite.
* @throws TransferException
*/
DeltaList sendManifest(Transfer transfer, File manifest) throws TransferException;
void sendManifest(Transfer transfer, File manifest, OutputStream results) throws TransferException;
/**
* Send the content of the specified urls

View File

@@ -21,7 +21,9 @@ package org.alfresco.repo.transfer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
@@ -32,6 +34,8 @@ import org.alfresco.service.cmr.transfer.TransferProgress;
import org.alfresco.service.cmr.transfer.TransferReceiver;
import org.alfresco.service.cmr.transfer.TransferTarget;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This class delegates transfer service to the transfer receiver without
@@ -44,6 +48,8 @@ import org.alfresco.service.transaction.TransactionService;
*/
public class UnitTestInProcessTransmitterImpl implements TransferTransmitter
{
private static final Log log = LogFactory.getLog(UnitTestInProcessTransmitterImpl.class);
private TransferReceiver receiver;
private ContentService contentService;
@@ -118,20 +124,34 @@ public class UnitTestInProcessTransmitterImpl implements TransferTransmitter
}
}
public DeltaList sendManifest(Transfer transfer, File manifest) throws TransferException
public void sendManifest(Transfer transfer, File manifest, OutputStream result) throws TransferException
{
try
{
String transferId = transfer.getTransferId();
FileInputStream fs = new FileInputStream(manifest);
receiver.saveSnapshot(transferId, fs);
// Now get the requsite
try
{
receiver.generateRequsite(transferId, result);
result.close();
return;
}
catch(IOException ie)
{
log.error("Error in unit test code: should not get this", ie);
return;
}
}
catch (FileNotFoundException error)
{
throw new TransferException("test error", error);
}
return null;
}
public void verifyTarget(TransferTarget target) throws TransferException
@@ -159,6 +179,5 @@ public class UnitTestInProcessTransmitterImpl implements TransferTransmitter
{
return contentService;
}
}

View File

@@ -29,7 +29,7 @@ public interface ManifestModel extends TransferModel
static final String LOCALNAME_TRANSFER_HEADER = "transferManifestHeader";
static final String LOCALNAME_HEADER_CREATED_DATE = "createdDate";
static final String LOCALNAME_HEADER_NODE_COUNT = "nodeCount";
static final String LOCALNAME_HEADER_COMPLETE = "complete";
static final String LOCALNAME_HEADER_SYNC = "sync";
static final String LOCALNAME_HEADER_REPOSITORY_ID = "repositoryId";
static final String LOCALNAME_ELEMENT_NODES = "nodes";
static final String LOCALNAME_ELEMENT_NODE = "node";

View File

@@ -31,7 +31,7 @@ public class TransferManifestHeader
private Date createdDate;
private int nodeCount;
private String repositoryId;
private boolean isComplete;
private boolean isSync;
public void setCreatedDate(Date createDate)
{
@@ -77,14 +77,14 @@ public class TransferManifestHeader
return repositoryId;
}
public void setComplete(boolean isComplete)
public void setSync(boolean isSync)
{
this.isComplete = isComplete;
this.isSync = isSync;
}
public boolean isComplete()
public boolean isSync()
{
return isComplete;
return isSync;
}

View File

@@ -387,10 +387,10 @@ public class XMLTransferManifestReader extends DefaultHandler implements Content
header.setNodeCount(Integer.parseInt(buffer.toString()));
buffer = null;
}
else if(elementName.equals(ManifestModel.LOCALNAME_HEADER_COMPLETE))
else if(elementName.equals(ManifestModel.LOCALNAME_HEADER_SYNC))
{
TransferManifestHeader header = (TransferManifestHeader)props.get("header");
header.setComplete(true);
header.setSync(true);
}
else if(elementName.equals(ManifestModel.LOCALNAME_HEADER_REPOSITORY_ID))
{

View File

@@ -158,15 +158,15 @@ public class XMLTransferManifestWriter implements TransferManifestWriter
+ ManifestModel.LOCALNAME_HEADER_REPOSITORY_ID);
}
if(header.isComplete())
if(header.isSync())
{
// Is this a complete transfer
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI,
ManifestModel.LOCALNAME_HEADER_COMPLETE, PREFIX + ":"
+ ManifestModel.LOCALNAME_HEADER_COMPLETE, EMPTY_ATTRIBUTES);
ManifestModel.LOCALNAME_HEADER_SYNC, PREFIX + ":"
+ ManifestModel.LOCALNAME_HEADER_SYNC, EMPTY_ATTRIBUTES);
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI,
ManifestModel.LOCALNAME_HEADER_COMPLETE, PREFIX + ":"
+ ManifestModel.LOCALNAME_HEADER_COMPLETE);
ManifestModel.LOCALNAME_HEADER_SYNC, PREFIX + ":"
+ ManifestModel.LOCALNAME_HEADER_SYNC);
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.transfer.requisite;
import org.alfresco.repo.transfer.DeltaList;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.xml.sax.SAXException;
/**
* A processor of the XML Transfer Requsite file to populate a DeltaList object
*
* The requsite is parsed once and the delta list is available from getDeltaList at the end.
*
* @author mrogers
*
*/
public class DeltaListRequsiteProcessor implements TransferRequsiteProcessor
{
DeltaList deltaList = null;
public void missingContent(NodeRef node, QName qname, String name)
{
deltaList.getRequiredURLs().add(name);
}
public void startTransferRequsite()
{
deltaList = new DeltaList();
}
public void endTransferRequsite()
{
// No op
}
/**
* Get the delta list
* @return the delta list or null if the XML provided does not contain the data.
*/
public DeltaList getDeltaList()
{
return deltaList;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.transfer.requisite;
import org.alfresco.repo.transfer.TransferModel;
/**
* The transfer model - extended for XML Manifest Model
*/
public interface RequsiteModel extends TransferModel
{
static final String LOCALNAME_TRANSFER_REQUSITE = "transferRequsite";
static final String LOCALNAME_ELEMENT_NODES = "nodes";
static final String LOCALNAME_ELEMENT_NODE = "node";
static final String LOCALNAME_ELEMENT_CONTENT = "requiredContent";
static final String LOCALNAME_ELEMENT_GROUPS = "groups";
static final String LOCALNAME_ELEMENT_GROUP = "group";
static final String LOCALNAME_ELEMENT_USERS = "users";
static final String LOCALNAME_ELEMENT_USER = "user";
// Manifest file prefix
static final String REQUSITE_PREFIX = "xferreq";
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.transfer.requisite;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* Processor for transfer requsite file
* @author mrogers
*
*/
public interface TransferRequsiteProcessor
{
/**
* Called at the start of a transfer requsite
*/
public void startTransferRequsite();
/**
* Called at the end of a transfer requsite
*/
public void endTransferRequsite();
/**
* Called when a missing content property is found
* @param node
* @param qname
* @param name
*/
public void missingContent(NodeRef node, QName qname, String name);
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.transfer.requisite;
import java.io.Writer;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.xml.sax.SAXException;
/**
* Transfer Requsite Writer
*
* This class formats the transfer requsite and prints it to the specified writer
*
* It is a statefull object and writes one requsite at a time.
*
*/
public interface TransferRequsiteWriter
{
/**
*
* @param writer
*/
void startTransferRequsite() ;
/**
*
*/
void endTransferRequsite() ;
/**
*
*/
void missingContent(NodeRef nodeRef, QName qName, String name);
}

View File

@@ -0,0 +1,247 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.transfer.requisite;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* SAX XML Content Handler to read a transfer manifest XML Stream and
* delegate processing of the manifest to the specified TransferRequsiteProcessor
*
* @author Mark Rogers
*/
public class XMLTransferRequsiteReader extends DefaultHandler implements ContentHandler, NamespacePrefixResolver
{
private TransferRequsiteProcessor processor;
/**
* These are the namespaces used within the document - there may be a different mapping to
* the namespaces of the Data Dictionary.
*/
LinkedList<HashMap<String, String>> namespaces = new LinkedList<HashMap<String, String>>();
final String TRANSFER_URI = RequsiteModel.TRANSFER_MODEL_1_0_URI;
final String XMLNS_URI = "http://www.w3.org/XML/1998/namespace";
/*
* Current State of the parser
*/
private StringBuffer buffer;
private Map<String, Object>props = new HashMap<String, Object>();
/**
* Constructor
* @param processor
*/
public XMLTransferRequsiteReader(TransferRequsiteProcessor processor)
{
this.processor = processor;
// prefix to uri map
HashMap<String, String> namespace = new HashMap<String, String>();
namespace.put("xmlns", XMLNS_URI);
namespaces.add(namespace);
}
public void startPrefixMapping(String prefix, String uri) throws SAXException
{
HashMap<String, String> namespace = namespaces.get(0);
// prefix is key, URI is value
namespace.put(prefix, uri);
}
public void endPrefixMapping(String prefix) throws SAXException
{
HashMap<String, String> namespace = namespaces.get(0);
// prefix is key, URI is value
namespace.remove(prefix);
}
// Namespace Prefix Resolver implementation below
/**
* lookup the prefix for a URI e.g. TRANSFER_URI for xfer
*/
public String getNamespaceURI(String prefix) throws NamespaceException
{
for(HashMap<String, String> namespace : namespaces)
{
String uri = namespace.get(prefix);
if(uri != null)
{
return uri;
}
}
return null;
}
/**
* @param uri
* @return the prefix
*/
public Collection<String> getPrefixes(String namespaceURI) throws NamespaceException
{
Collection<String> prefixes = new HashSet<String>();
for(HashMap<String, String> namespace : namespaces)
{
for(Entry<String, String> entry : namespace.entrySet())
{
if (namespaceURI.equals(entry.getValue()))
{
prefixes.add(entry.getKey());
}
}
}
return prefixes;
}
public Collection<String> getPrefixes()
{
Collection<String> prefixes = new HashSet<String>();
for(HashMap<String, String> namespace : namespaces)
{
prefixes.addAll(namespace.keySet());
}
return prefixes;
}
public Collection<String> getURIs()
{
Collection<String> uris = new HashSet<String>();
for(HashMap<String, String> namespace : namespaces)
{
uris.addAll(namespace.values());
}
return uris;
}
public void startDocument() throws SAXException
{
processor.startTransferRequsite();
}
public void endDocument() throws SAXException
{
processor.endTransferRequsite();
}
/**
* Start Element
*/
public void startElement(String uri, String localName, String prefixName, Attributes atts)
throws SAXException
{
QName elementQName = QName.resolveToQName(this, prefixName);
HashMap<String, String> namespace = new HashMap<String, String>();
namespaces.addFirst(namespace);
/**
* Look for any namespace attributes
*/
for(int i = 0; i < atts.getLength(); i++)
{
QName attributeQName = QName.resolveToQName(this, atts.getQName(i));
if(attributeQName.getNamespaceURI().equals(XMLNS_URI))
{
namespace.put(attributeQName.getLocalName(), atts.getValue(i));
}
}
if(elementQName == null)
{
return;
}
if(elementQName.getNamespaceURI().equals(TRANSFER_URI));
{
// This is one of the transfer manifest elements
String elementName = elementQName.getLocalName();
// Simple and stupid parser for now
if(elementName.equals(RequsiteModel.LOCALNAME_TRANSFER_REQUSITE))
{
// Good we got this
}
else if(elementName.equals(RequsiteModel.LOCALNAME_ELEMENT_CONTENT))
{
NodeRef nodeRef = new NodeRef(atts.getValue("", "nodeRef"));
QName qname = QName.createQName(atts.getValue("", "qname"));
String name = atts.getValue("", "name");
processor.missingContent(nodeRef, qname, name);
}
} // if transfer URI
} // startElement
/**
* End Element
*/
@SuppressWarnings("unchecked")
public void endElement(String uri, String localName, String prefixName) throws SAXException
{
namespaces.removeFirst();
} // end element
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
{
//NO-OP
}
public void processingInstruction(String target, String data) throws SAXException
{
//NO-OP
}
public void setDocumentLocator(Locator locator)
{
//NO-OP
}
public void skippedEntity(String name) throws SAXException
{
//NO-OP
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.transfer.requisite;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.Writer;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.alfresco.repo.transfer.RepoRequsiteManifestProcessorImpl;
import org.alfresco.repo.transfer.TransferModel;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.MLText;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
import org.alfresco.service.cmr.transfer.TransferException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.extensions.surf.util.Base64;
import org.springframework.extensions.surf.util.ISO8601DateFormat;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/**
* Writes the transfer requsite out in XML format to the specified writer.
*
* XMLTransferRequsiteWriter is a statefull object used for writing out a single transfer requsite
* file in XML format to the writer passed in via startTransferRequsite.
*
* @author Mark Rogers
*/
public class XMLTransferRequsiteWriter implements TransferRequsiteWriter
{
private static final Log log = LogFactory.getLog(XMLTransferRequsiteWriter.class);
public XMLTransferRequsiteWriter(Writer out)
{
OutputFormat format = OutputFormat.createPrettyPrint();
format.setNewLineAfterDeclaration(false);
format.setIndentSize(3);
format.setEncoding("UTF-8");
this.writer = new XMLWriter(out, format);
}
private XMLWriter writer;
final AttributesImpl EMPTY_ATTRIBUTES = new AttributesImpl();
final String PREFIX = RequsiteModel.REQUSITE_PREFIX;
/**
* Start the transfer manifest
*/
public void startTransferRequsite()
{
try
{
this.writer.startDocument();
this.writer.startPrefixMapping(PREFIX, TransferModel.TRANSFER_MODEL_1_0_URI);
this.writer.startPrefixMapping("cm", NamespaceService.CONTENT_MODEL_1_0_URI);
// Start Transfer Manifest // uri, name, prefix
this.writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI,
RequsiteModel.LOCALNAME_TRANSFER_REQUSITE, PREFIX + ":"
+ RequsiteModel.LOCALNAME_TRANSFER_REQUSITE, EMPTY_ATTRIBUTES);
}
catch (SAXException se)
{
log.debug("error", se);
}
}
/**
* End the transfer manifest
*/
public void endTransferRequsite()
{
try
{
// End Transfer Manifest
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI,
RequsiteModel.LOCALNAME_TRANSFER_REQUSITE, PREFIX + ":"
+ RequsiteModel.LOCALNAME_TRANSFER_REQUSITE);
writer.endPrefixMapping(PREFIX);
writer.endDocument();
}
catch (SAXException se)
{
log.debug("error", se);
}
}
public void missingContent(NodeRef node, QName qname, String name)
{
log.debug("write missing content");
try
{
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute("uri", "nodeRef", "nodeRef", "String", node.toString());
attributes.addAttribute("uri", "qname", "qname", "String", qname.toString());
attributes.addAttribute("uri", "name", "name", "String", name.toString());
// Start Missing Content
this.writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI,
RequsiteModel.LOCALNAME_ELEMENT_CONTENT, PREFIX + ":"
+ RequsiteModel.LOCALNAME_ELEMENT_CONTENT, attributes);
// Missing Content
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI,
RequsiteModel.LOCALNAME_ELEMENT_CONTENT, PREFIX + ":"
+ RequsiteModel.LOCALNAME_ELEMENT_CONTENT);
}
catch (SAXException se)
{
log.debug("error", se);
}
}
}

View File

@@ -0,0 +1,9 @@
/**
* Provides the implementation of the transfer requsite which is used by the transfer service.
* <p>
* XMLTransferRequsiteWriter writes the transfer requsite. XMLTransferRequsiteReader reads the transfer requsite and calls the
* TransferRequsiteProcessor as the read progresses. These classes are designed to stream content through, processing each node at a time,
* and not hold a large data objects in memory.
* @since 3.4
*/
package org.alfresco.repo.transfer.requisite;

View File

@@ -31,10 +31,11 @@ import org.alfresco.service.cmr.repository.NodeRef;
*
* nodes Specifies which node to transfer
* <p>
* isComplete specifies whether the list of nodes is complete. If complete then the transfer
* machinery can determine by the absence of a node that the missing node should be deleted.
* if the transfer is not complete (a partial transfer) then the archive node ref is required
* to delete a remote node.
* isSync specifies whether the list of nodes is to be sync'ed. If sync then the transfer
* machinery can determine by the absence of a node or association in the transfer that the missing
* nodes should be deleted on the destination.
* Else with a non sync transfer then the archive node ref is required to remote a node on the destination.
*
*
*/
public class TransferDefinition implements Serializable
@@ -48,7 +49,7 @@ public class TransferDefinition implements Serializable
private Set<NodeRef> nodes;
// is complete
private boolean isComplete = false;
private boolean isSync = false;
/**
* Set which nodes to transfer
@@ -74,23 +75,25 @@ public class TransferDefinition implements Serializable
}
/**
* isComplete specifies whether the list of nodes is complete. If complete then the transfer
* machinery can determine by the absence of a node in the transfer that the missing node should be deleted.
* Else with a partial transfer then the archive node ref is required to delete a remote node.
* isSync specifies whether the list of nodes is to be sync'ed. If sync then the transfer
* machinery can determine by the absence of a node or association in the transfer that the missing
* nodes should be deleted on the destination.
* Else with a non sync transfer then the archive node ref is required to remote a node on the destination.
*/
public void setComplete(boolean isComplete)
public void setSync(boolean isSync)
{
this.isComplete = isComplete;
this.isSync = isSync;
}
/**
* isComplete specifies whether the list of nodes is complete. If complete then the transfer
* machinery can determine by the absence of a node in the transfer that the missing node should be deleted.
* Else with a partial transfer then the archive node ref is required to delete a remote node.
* @return true if the transfer contains a full list of dependent nodes.
* isSync specifies whether the list of nodes is to be sync'ed. If sync then the transfer
* machinery can determine by the absence of a node or association in the transfer that missing
* nodes should be deleted on the destination.
* Else with a non sync transfer then the archive node ref is required to remote a node on the destination.
* @return true if the transfer is in "sync" mode.
*/
public boolean isComplete()
public boolean isSync()
{
return isComplete;
return isSync;
}
}

View File

@@ -21,6 +21,7 @@ package org.alfresco.service.cmr.transfer;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import org.alfresco.repo.transfer.TransferProgressMonitor;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -77,8 +78,22 @@ public interface TransferReceiver
*/
void saveSnapshot(String transferId, InputStream snapshotStream) throws TransferException;
/**
* Save a content item
* @param transferId
* @param contentId
* @param contentStream
* @throws TransferException
*/
void saveContent(String transferId, String contentId, InputStream contentStream) throws TransferException;
/**
* Write the requsite (the bits required to support the Manifest) to the output stream.
* @param requsiteStream an open stream to receive the requisite
* @throws TransferException
*/
void generateRequsite(String transferId, OutputStream requsiteStream) throws TransferException;
/**
* Prepare
* @param transferId