Merge DM-DM_deployment to HEAD

17716 : Copied alfresco/HEAD to alfresco/BRANCHES/DEV/BRIAN/DM-DM_deployment.
  17721 : Moved alfresco/BRANCHES/DEV/BRIAN/DM-DM_deployment to alfresco/BRANCHES/DEV/DM-DM_deployment.
  17809 : Bare Bones of TransferService.
  17813 : more bones
  17824 : bootstrap of the transfer spaces
  17826 : Added username, path, and a getter for the password.
  17832 : createTransferTarget, getTransferTargets() working.
  17836 : transfer target 
    - duplicate name detection
    - delete transfer target by name
    - get transfer target by name
    - TransferException added along with transfer-service.properties
  17840 : transfer target
   - enable / disable
   - update properties
  17851 : Added the notion of a transmitter for the transfer service, and an HttpClient implementation of it.
  17852 : Added the web script needed for the receiving end of a transfer, along with a command processor for clients to 
check availability of the service and credentials.
  17856 : Added a Mockito-based test for HttpClientTransmitterImpl
  17857 : Corrected javadoc for test class
  17858 : Added test for overriding SSL socket factory
  17859 : Wired up the transfer service to the transfer transmitter.
  17896 : Rework to spring for app:transferDefinitions it's now app:transfer_definitio
    - Add throws clauses to TransferService
    - Bare bones of interface for begin and sendManifest
  17944 : Work in progress on manifest file
  17947 : added parent and child associations to the snapshot.
  17956 : Now has the parent path implemented and introduces the TransferManifestNodeFactory.
  17965 : Added content, MLText and collections.
  17978 : addition of source and target peer associations.
  17982 : Fixing parentNode information.
  18008 : XML Manifest Reader checkpoint. (Still incomplete but lots working)
  18040 : ParentPath is transmitted, fixes for source and target assocs.
  18048 : SAIL-30: Initial commit of functionality for begin, sendManifest, and sendContent parts of the transfer process
  18049 : Fix for parse of Locale type.
  18054 : Added TransferManifestNodeHelper and more tests
  18066 : Work in progress check in
   - Implemented the content chunker
 - sketched out more interfaces on TransferTransmitter.
 - Please note that the chunker is not yet connected to the manifest file, that will come next.
r18069 : Wired up manifest reader to content chunker.
r18089 : Fiest cut of callback interface for review.
r18091 : added hashCode implementation which was missing from ContentData
r18095 : Start of the server-side commit. Note that this is an interim commit - not tested.
r18096 : Initial entry of a ContentData implementation of HttpClient's "Part"
r18155 : Work in progress check in.
    TransferEvent - incomplete
    HttpClientTransmitter - first cut complete (not tested)
    Server side - first cut complete (not tested)
  18156 : TransferMessage missing from last check in.
  18166 : check in command processors
  18167 : Work primarily on the transfer commit operation
  18170 : corrected spring errors.
  18176 : Further testing and fixing of transfer commit
  18206 : Work in progress.
  18236 : Work in progress - generally adding debug logging and sorting out exception handlers.
  18240 : Fix to call "end" correctly after exception is thrown with "commit"
  18242 : Aligning the manifest part names.
  18243 : PostSnapshot calls the correct method on the receiver service.
  18267 : First node has transferred.
  18274 : Fixing abort to call end, debug statements, formatting code brackest
  18275 : First code to handle updates. Also improved error messages passed back to client
  18289 : Checked in work in progress.  Content upload not working.
  18290 : Update to ensure file type.
  18300 : Added more log output and some of the error messages.
  18301 : Work in progress
  18302 : Added log output
  18307 : Added a noddy transfer action
  18315 : Sprint 2 complete - transfer and update one node.
  18354 : Now the manifest file has deleted nodes.
 - Adding copyright headers
  18384 : Plumbing for unit tests on one box.
  18416 : First end to end unit test working.   (one node create and update)
  18421 : Added path based update test and many send test.
  18458 : Added the functionality to transfer deleted and restored nodes.
  18481 : Implementation of transferAsync
  18491 : SAIL-32, SAIL-35
   - Added node crawler for DM-DM Transfer F6 and F9.
  18620 : Basic transfer report implementation

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@18858 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Mark Rogers
2010-02-25 20:07:09 +00:00
parent e0f29a76e4
commit af1a735c98
93 changed files with 12954 additions and 20 deletions

View File

@@ -16,6 +16,7 @@
--> -->
<import resource="classpath:alfresco/application-context-highlevel.xml" /> <import resource="classpath:alfresco/application-context-highlevel.xml" />
<import resource="classpath*:alfresco/transfer-service-context.xml"/>
<!-- <!--
Import activation extensions for Multi-Tenancy. Import activation extensions for Multi-Tenancy.

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<view:view
xmlns:view="http://www.alfresco.org/view/repository/1.0"
xmlns:cm="http://www.alfresco.org/model/content/1.0"
xmlns:app="http://www.alfresco.org/model/application/1.0"
xmlns:trx="http://www.alfresco.org/model/transfer/1.0">
<cm:folder view:childName="${spaces.transfers.childname}">
<view:aspects>
</view:aspects>
<view:properties>
<cm:description>Folder used by the Transfer subsystem.</cm:description>
<app:icon>space-icon-default</app:icon>
<cm:title>Transfers</cm:title>
<cm:name>Transfers</cm:name>
</view:properties>
<cm:contains>
<cm:folder view:childName="${spaces.transfer_groups.childname}">
<view:aspects>
</view:aspects>
<view:properties>
<cm:description>Folder containing groups of transfer targets.</cm:description>
<app:icon>space-icon-default</app:icon>
<cm:title>Transfer Target Groups</cm:title>
<cm:name>Transfer Target Groups</cm:name>
</view:properties>
<cm:contains>
<trx:transferGroup view:childName="cm:default">
<view:aspects>
</view:aspects>
<view:properties>
<cm:description>Default Group.</cm:description>
<app:icon>space-icon-default</app:icon>
<cm:title>Default Group</cm:title>
<cm:name>Default Group</cm:name>
</view:properties>
</trx:transferGroup>
</cm:contains>
</cm:folder>
<cm:folder view:childName="${spaces.inbound_transfer_records.childname}">
<view:aspects>
</view:aspects>
<view:properties>
<cm:description>Folder containing records of inbound transfers.</cm:description>
<app:icon>space-icon-default</app:icon>
<cm:title>Inbound Transfer Records</cm:title>
<cm:name>Inbound Transfer Records</cm:name>
</view:properties>
</cm:folder>
<cm:folder view:childName="${spaces.outbound_transfer_records.childname}">
<view:aspects>
</view:aspects>
<view:properties>
<cm:description>Folder containing records of outbound transfers.</cm:description>
<app:icon>space-icon-default</app:icon>
<cm:title>Outbound Transfer Records</cm:title>
<cm:name>Outbound Transfer Records</cm:name>
</view:properties>
</cm:folder>
<cm:folder view:childName="${spaces.transfer_temp.childname}">
<view:aspects>
</view:aspects>
<view:properties>
<cm:description>Folder to store temporary nodes during transfer.</cm:description>
<app:icon>space-icon-default</app:icon>
<cm:title>Temp</cm:title>
<cm:name>Temp</cm:name>
</view:properties>
</cm:folder>
</cm:contains>
</cm:folder>
</view:view>

View File

@@ -1072,6 +1072,7 @@
<value>alfresco/model/wcmModel.xml</value> <value>alfresco/model/wcmModel.xml</value>
<value>alfresco/model/forumModel.xml</value> <value>alfresco/model/forumModel.xml</value>
<value>alfresco/model/imapModel.xml</value> <value>alfresco/model/imapModel.xml</value>
<value>alfresco/model/transferModel.xml</value>
<!-- Content models --> <!-- Content models -->
<value>alfresco/model/applicationModel.xml</value> <value>alfresco/model/applicationModel.xml</value>

View File

@@ -348,6 +348,11 @@
<prop key="spaces.templates.email.invite1.childname">${spaces.templates.email.invite1.childname}</prop> <prop key="spaces.templates.email.invite1.childname">${spaces.templates.email.invite1.childname}</prop>
<prop key="spaces.templates.email.notify.childname">${spaces.templates.email.notify.childname}</prop> <prop key="spaces.templates.email.notify.childname">${spaces.templates.email.notify.childname}</prop>
<prop key="spaces.imapConfig.childname">${spaces.imapConfig.childname}</prop> <prop key="spaces.imapConfig.childname">${spaces.imapConfig.childname}</prop>
<prop key="spaces.transfers.childname">${spaces.transfers.childname}</prop>
<prop key="spaces.transfer_groups.childname">${spaces.transfer_groups.childname}</prop>
<prop key="spaces.transfer_temp.childname">${spaces.transfer_temp.childname}</prop>
<prop key="spaces.inbound_transfer_records.childname">${spaces.inbound_transfer_records.childname}</prop>
<prop key="spaces.outbound_transfer_records.childname">${spaces.outbound_transfer_records.childname}</prop>
<prop key="spaces.imap_templates.childname">${spaces.imap_templates.childname}</prop> <prop key="spaces.imap_templates.childname">${spaces.imap_templates.childname}</prop>
<prop key="spaces.emailActions.childname">${spaces.emailActions.childname}</prop> <prop key="spaces.emailActions.childname">${spaces.emailActions.childname}</prop>
<prop key="spaces.searchAction.childname">${spaces.searchAction.childname}</prop> <prop key="spaces.searchAction.childname">${spaces.searchAction.childname}</prop>
@@ -554,6 +559,12 @@
<prop key="location">alfresco/bootstrap/imapSpaces.xml</prop> <prop key="location">alfresco/bootstrap/imapSpaces.xml</prop>
<prop key="messages">alfresco/messages/bootstrap-spaces</prop> <prop key="messages">alfresco/messages/bootstrap-spaces</prop>
</props> </props>
<props>
<prop key="path">/${spaces.company_home.childname}/${spaces.dictionary.childname}</prop>
<prop key="location">alfresco/bootstrap/transferSpaces.xml</prop>
<prop key="messages">alfresco/messages/bootstrap-spaces</prop>
</props>
</list> </list>
</property> </property>
</bean> </bean>

View File

@@ -289,3 +289,6 @@ patch.personUsagePatch.result2=No people were missing the 'cm:sizeCurrent' prope
patch.redeployNominatedInvitationProcessWithPropsForShare.description=Redeploy nominated invitation workflow patch.redeployNominatedInvitationProcessWithPropsForShare.description=Redeploy nominated invitation workflow
patch.redeployNominatedInvitationProcessWithPropsForShare.result=Nominated invitation workflow redeployed patch.redeployNominatedInvitationProcessWithPropsForShare.result=Nominated invitation workflow redeployed
patch.transferDefinitions.description=Add transfer definitions folder to data dictionary.
patch.transferDefinitions.result=Transfer definitions folder added to data dictionary.

View File

@@ -0,0 +1,22 @@
# Transfer service externalised display strings
transfer_service.unable_to_find_transfer_home=Unable to find transfer home: {0}
transfer_service.unable_to_find_transfer_group=Unable to find transfer group with name, {0}
transfer_service.unable_to_find_transfer_target=Unable to find transfer target with name, {0}
transfer_service.target_exists=Unable to create a new transfer target with the name, {0}, since there is already a target with that name.
transfer_service.comms.unsupported_protocol=Unsupported protocol: {0}
transfer_service.comms.unsuccessful_response=Received unsuccessful response code from target server: {0}, {1}
transfer_service.comms.http_request_failed=Failed to execute HTTP request {0} to target: {1} status: {2}
transfer_service.no_nodes=No nodes to transfer
transfer_service.receiver.failed_to_create_staging_folder=Unable to create staging directory for transfer {0}
transfer_service.receiver.lock_folder_not_found=Unable to locate specified lock folder: {0}
transfer_service.receiver.temp_folder_not_found=Unable to locate specified temporary folder for transfer {0}: {1}
transfer_service.receiver.lock_unavailable=Transfer lock has been claimed for another inbound transfer. Unable to start new transfer.
transfer_service.receiver.record_folder_not_found=Failed to find folder specified to hold inbound transfer records: {0}
transfer_service.receiver.not_lock_owner=Failed attempt to carry out transfer operation. Lock not held by specified transfer: {0}
transfer_service.receiver.error_ending_transfer=
transfer_service.receiver.error_staging_snapshot=
transfer_service.receiver.error_staging_content=
transfer_service.receiver.no_snapshot_received=
transfer_service.receiver.error_committing_transfer=

View File

@@ -0,0 +1,134 @@
<model name="trx:applicationmodel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<description>Alfresco Transfer Application Model</description>
<author>Alfresco</author>
<published>2009-12-16</published>
<version>1.0</version>
<imports>
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
<import uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/>
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
</imports>
<namespaces>
<namespace uri="http://www.alfresco.org/model/transfer/1.0" prefix="trx"/>
</namespaces>
<constraints>
<constraint name="trx:protocols" type="LIST">
<parameter name="allowedValues">
<list>
<value>http</value>
<value>https</value>
</list>
</parameter>
</constraint>
</constraints>
<types>
<type name="trx:transferGroup">
<title>Transfer Group</title>
<description>The definition of a transfer group</description>
<parent>cm:folder</parent>
<properties>
</properties>
</type>
<type name="trx:transferTarget">
<title>Transfer Target</title>
<description>The definition of a transfer target</description>
<parent>cm:folder</parent>
<properties>
<property name="trx:endpointhost">
<title>Endpoint Host</title>
<type>d:text</type>
<mandatory enforced="true">true</mandatory>
<index enabled="true">
<atomic>true</atomic>
<stored>false</stored>
<tokenised>false</tokenised>
</index>
</property>
<property name="trx:endpointport">
<title>Endpoint Port</title>
<type>d:int</type>
<mandatory enforced="true">true</mandatory>
</property>
<property name="trx:endpointpath">
<title>Endpoint Path</title>
<type>d:text</type>
<mandatory enforced="true">true</mandatory>
</property>
<property name="trx:endpointprotocol">
<title>Endpoint Protocol</title>
<type>d:text</type>
<mandatory enforced="true">true</mandatory>
<constraints>
<constraint ref="trx:protocols" />
</constraints>
</property>
<property name="trx:username">
<title>Username</title>
<type>d:text</type>
</property>
<!-- todo cleartext for now - needs encrypting -->
<property name="trx:password">
<title>Password</title>
<type>d:any</type>
<index enabled="false">
<atomic>true</atomic>
<stored>false</stored>
<tokenised>false</tokenised>
</index>
</property>
</properties>
</type>
<type name="trx:transferLock">
<title>Transfer Lock</title>
<description>Node type used to represent the transfer lock node</description>
<parent>cm:content</parent>
<properties>
<property name="trx:transferId">
<title>Locked Transfer Identifier</title>
<type>d:text</type>
<mandatory enforced="false">true</mandatory>
</property>
</properties>
</type>
<!-- The Transfer Report -->
<type name="trx:transferReport">
<title>Transfer Report</title>
<description>Transfer Report</description>
<parent>cm:content</parent>
</type>
</types>
<aspects>
<!-- Can this resorce be temporarily disabled? -->
<aspect name="trx:enableable">
<title>Can this resource be enabled/disabled.</title>
<properties>
<property name="trx:enabled">
<title>Is this enabled.</title>
<type>d:boolean</type>
<mandatory>true</mandatory>
</property>
</properties>
</aspect>
</aspects>
</model>

View File

@@ -2020,4 +2020,26 @@
</property> </property>
</bean> </bean>
<bean id="patch.transferDefinitionsFolder" class="org.alfresco.repo.admin.patch.impl.GenericBootstrapPatch" parent="basePatch" >
<property name="id"><value>patch.transferServiceFolder</value></property>
<property name="description"><value>patch.transferDefinitions.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>3007</value></property>
<property name="targetSchema"><value>3008</value></property>
<!-- bootstrap view -->
<property name="importerBootstrap">
<ref bean="spacesBootstrap" />
</property>
<property name="checkPath">
<value>/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname}</value>
</property>
<property name="bootstrapView">
<props>
<prop key="path">/${spaces.company_home.childname}/${spaces.dictionary.childname}</prop>
<prop key="location">alfresco/bootstrap/transferSpaces.xml</prop>
<prop key="messages">alfresco/messages/bootstrap-spaces</prop>
</props>
</property>
</bean>
</beans> </beans>

View File

@@ -313,6 +313,11 @@ spaces.user_homes.childname=app:user_homes
spaces.sites.childname=st:sites spaces.sites.childname=st:sites
spaces.templates.email.invite.childname=cm:invite spaces.templates.email.invite.childname=cm:invite
spaces.wcm_deployed.childname=cm:wcm_deployed spaces.wcm_deployed.childname=cm:wcm_deployed
spaces.transfers.childname=app:transfers
spaces.transfer_groups.childname=app:transfer_groups
spaces.transfer_temp.childname=app:temp
spaces.inbound_transfer_records.childname=app:inbound_transfer_records
spaces.outbound_transfer_records.childname=app:outbound_transfer_records
# ADM VersionStore Configuration # ADM VersionStore Configuration
version.store.deprecated.lightWeightVersionStore=workspace://lightWeightVersionStore version.store.deprecated.lightWeightVersionStore=workspace://lightWeightVersionStore
@@ -447,3 +452,6 @@ deployment.service.numberOfSendingThreads=5
deployment.service.corePoolSize=2 deployment.service.corePoolSize=2
deployment.service.maximumPoolSize=3 deployment.service.maximumPoolSize=3
# Transfer Service
transferservice.receiver.enabled=true
transferservice.receiver.stagingDir=${java.io.tmpdir}/alfresco-transfer-staging

View File

@@ -0,0 +1,124 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<!-- Transfer Service Configuration -->
<bean id="transferService" class="org.alfresco.repo.transfer.TransferServiceImpl" init-method="init">
<property name="actionService">
<ref bean="ActionService"/>
</property>
<property name="nodeService">
<ref bean="NodeService"/>
</property>
<property name="searchService">
<ref bean="SearchService"/>
</property>
<property name="transactionService">
<ref bean="TransactionService"/>
</property>
<property name="transferSpaceQuery">
<value>/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname}/${spaces.transfer_groups.childname}</value>
</property>
<property name="defaultTransferGroup">
<value>Default Group</value>
</property>
<property name="transmitter" ref="transferTransmitter" />
<property name="transferManifestNodeFactory" ref="transferManifestNodeFactory" />
<property name="transferReporter" ref="transferReporter" />
</bean>
<bean id="transferTransmitter" class="org.alfresco.repo.transfer.HttpClientTransmitterImpl" init-method="init" >
<property name="contentService" ref="ContentService" />
</bean>
<bean id="transferReporter" class="org.alfresco.repo.transfer.report.TransferReporterImpl" init-method="init" >
<property name="contentService" ref="ContentService" />
<property name="nodeService" ref="NodeService" />
</bean>
<bean id="transferManifestNodeFactory" class="org.alfresco.repo.transfer.manifest.TransferManifestNodeFactoryImpl" init-method="init" >
<property name="nodeService" ref="NodeService" />
</bean>
<bean id="transferReceiver" class="org.alfresco.repo.transfer.RepoTransferReceiverImpl" init-method="init">
<property name="nodeService" ref="NodeService" />
<property name="searchService" ref="SearchService" />
<property name="transactionService" ref="TransactionService" />
<property name="transferLockFolderPath"><value>/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname}</value></property>
<property name="transferTempFolderPath"><value>/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname}/${spaces.transfer_temp.childname}</value></property>
<property name="inboundTransferRecordsPath"><value>/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname}/${spaces.inbound_transfer_records.childname}</value></property>
<property name="rootStagingDirectory"><value>${transferservice.receiver.stagingDir}</value></property>
<property name="manifestProcessorFactory" ref="transferManifestProcessorFactory" />
<property name="behaviourFilter" ref="policyBehaviourFilter" />
</bean>
<bean id="transferManifestProcessorFactory" class="org.alfresco.repo.transfer.DefaultManifestProcessorFactoryImpl">
<property name="nodeService" ref="NodeService" />
<property name="contentService" ref="ContentService" />
<property name="nodeResolverFactory" ref="transferNodeResolverFactory" />
</bean>
<bean id="transferNodeResolverFactory" class="org.alfresco.repo.transfer.DefaultCorrespondingNodeResolverFactory">
<property name="nodeService" ref="NodeService" />
</bean>
<!-- I18N -->
<bean id="transferServiceResourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
<property name="resourceBundles">
<list>
<value>alfresco.messages.transfer-service</value>
</list>
</property>
</bean>
<bean id="TransferService_security" class="org.alfresco.repo.security.permissions.impl.AlwaysProceedMethodInterceptor" />
<bean id="TransferService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">${server.transaction.mode.default}</prop>
</props>
</property>
</bean>
<!-- Transfer service bean -->
<bean id="TransferService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.service.cmr.transfer.TransferService</value>
</property>
<property name="target">
<ref bean="transferService"/>
</property>
<property name="interceptorNames">
<list>
<idref local="TransferService_transaction"/>
<idref bean="AuditMethodInterceptor"/>
<idref bean="exceptionTranslator"/>
<idref local="TransferService_security"/>
</list>
</property>
</bean>
<!-- Action for transfer async -->
<bean id="transfer-async" class="org.alfresco.repo.transfer.TransferAsyncAction" parent="action-executer">
<!-- Run transferAsyncAction on the deployment queue -->
<property name="queueName">
<value>deployment</value>
</property>
<property name="transferService">
<ref bean="TransferService"/>
</property>
<property name="publicAction">
<value>false</value>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.List;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.Path.Element;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author brian
*
*/
public class BasicCorrespondingNodeResolverImpl implements CorrespondingNodeResolver
{
private static final Log log = LogFactory.getLog(BasicCorrespondingNodeResolverImpl.class);
private static final String MSG_SPECIFIED_STORE_DOES_NOT_EXIST = "transfer_service.receiver.specified_store_nonexistent";
private NodeService nodeService;
public ResolvedParentChildPair resolveCorrespondingNode(NodeRef sourceNodeRef, ChildAssociationRef primaryAssoc,
Path parentPath)
{
if (log.isDebugEnabled()) {
log.debug("Attempting to resolve corresponding node for noderef " + sourceNodeRef);
log.debug("Supplied parent path: " + parentPath);
log.debug("Supplied parent assoc: " + primaryAssoc.toString());
}
ResolvedParentChildPair result = new ResolvedParentChildPair(null, null);
// Does a node with the same NodeRef already exist in this repo?
if (nodeService.exists(sourceNodeRef))
{
result.resolvedChild = sourceNodeRef;
}
// Find where this node should live
NodeRef parentNodeRef = primaryAssoc.getParentRef();
if (!nodeService.exists(parentNodeRef.getStoreRef()))
{
throw new TransferProcessingException(MSG_SPECIFIED_STORE_DOES_NOT_EXIST);
}
if (!nodeService.exists(parentNodeRef))
{
if (log.isDebugEnabled())
{
log.debug("Unable to find node's parent by node ref: " + parentNodeRef);
}
parentNodeRef = resolveParentPath(primaryAssoc.getParentRef().getStoreRef(), parentPath);
}
else
{
if (log.isDebugEnabled())
{
log.debug("Node's parent has been resolved by noderef: " + parentNodeRef);
}
}
if (log.isDebugEnabled())
{
log.debug("Parent noderef resolved to node: " + parentNodeRef);
}
result.resolvedParent = parentNodeRef;
if ((parentNodeRef != null) && (result.resolvedChild == null))
{
//We've managed to find the approprate parent node, but not the child.
//See if we can find the child by looking at the parent's child associations
List<ChildAssociationRef> children = nodeService.getChildAssocs(parentNodeRef, RegexQNamePattern.MATCH_ALL,
primaryAssoc.getQName());
if (!children.isEmpty())
{
result.resolvedChild = children.get(0).getChildRef();
}
}
if (log.isDebugEnabled())
{
log.debug("Resolved child node to: " + result.resolvedChild);
}
return result;
}
/**
* @param parentPath
*/
private NodeRef resolveParentPath(StoreRef store, Path parentPath)
{
if (log.isDebugEnabled())
{
log.debug("Trying to resolve parent path " + parentPath);
}
NodeRef node = nodeService.getRootNode(store);
int index = 1;
while (index < parentPath.size())
{
Element element = parentPath.get(index++);
QName name = QName.createQName(element.getElementString());
List<ChildAssociationRef> children = nodeService.getChildAssocs(node, RegexQNamePattern.MATCH_ALL, name);
if (children.isEmpty())
{
if (log.isDebugEnabled())
{
log.debug("Failed to resolve path element " + element.getElementString());
}
return null;
}
if (log.isDebugEnabled())
{
log.debug("Resolved path element " + element.getElementString());
}
node = children.get(0).getChildRef();
}
return node;
}
/**
* @param nodeService the nodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Path;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author brian
*
*/
public class CachingCorrespondingNodeResolverImpl implements CorrespondingNodeResolver
{
private static final Log log = LogFactory.getLog(CachingCorrespondingNodeResolverImpl.class);
private Map<NodeRef, ResolvedParentChildPair> cache = new HashMap<NodeRef, ResolvedParentChildPair>(359);
private CorrespondingNodeResolver delegateResolver;
public CachingCorrespondingNodeResolverImpl()
{
}
/**
* @param delegateResolver
*/
public CachingCorrespondingNodeResolverImpl(CorrespondingNodeResolver delegateResolver)
{
super();
this.delegateResolver = delegateResolver;
}
public ResolvedParentChildPair resolveCorrespondingNode(NodeRef sourceNodeRef, ChildAssociationRef primaryAssoc,
Path parentPath)
{
ResolvedParentChildPair result = cache.get(sourceNodeRef);
if (result != null)
{
if (log.isDebugEnabled())
{
log.debug("Found fully-resolved entry in cache for node " + sourceNodeRef);
}
return result;
}
result = delegateResolver.resolveCorrespondingNode(sourceNodeRef, primaryAssoc, parentPath);
//If we have fully resolved the parent and child nodes then stick it in the cache...
if (result.resolvedChild != null && result.resolvedParent != null)
{
cache.put(sourceNodeRef, result);
}
return result;
}
/**
* @param delegateResolver the delegateResolver to set
*/
public void setDelegateResolver(CorrespondingNodeResolver delegateResolver)
{
this.delegateResolver = delegateResolver;
}
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.transfer.NodeFinder;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.ParameterCheck;
/**
* @author brian
*
*/
public class ChildAssociatedNodeFinder implements NodeFinder
{
private Set<QName> suppliedAssociationTypes = new HashSet<QName>();
private boolean exclude = false;
private boolean initialised = false;
private List<QName> childAssociationTypes = new ArrayList<QName>();
private ServiceRegistry serviceRegistry;
public ChildAssociatedNodeFinder()
{
}
public ChildAssociatedNodeFinder(Set<QName> associationTypeNames)
{
setAssociationTypes(associationTypeNames);
}
public ChildAssociatedNodeFinder(QName... associationTypeNames)
{
setAssociationTypes(associationTypeNames);
}
public ChildAssociatedNodeFinder(Set<QName> associationTypeNames, boolean exclude)
{
setAssociationTypes(associationTypeNames);
this.exclude = exclude;
}
public ChildAssociatedNodeFinder(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
public ChildAssociatedNodeFinder(ServiceRegistry serviceRegistry, Set<QName> associationTypeNames)
{
this(serviceRegistry);
setAssociationTypes(associationTypeNames);
}
public ChildAssociatedNodeFinder(ServiceRegistry serviceRegistry, QName... associationTypeNames)
{
this(serviceRegistry);
setAssociationTypes(associationTypeNames);
}
public ChildAssociatedNodeFinder(ServiceRegistry serviceRegistry, Set<QName> associationTypeNames, boolean exclude)
{
setAssociationTypes(associationTypeNames);
this.exclude = exclude;
}
public void setAssociationTypes(QName... associationTypes)
{
setAssociationTypes(Arrays.asList(associationTypes));
}
public void setAssociationTypes(Collection<QName> associationTypes)
{
this.suppliedAssociationTypes = new HashSet<QName>(associationTypes);
initialised = false;
}
/**
* @param exclude
* the exclude to set
*/
public void setExclude(boolean exclude)
{
this.exclude = exclude;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.service.cmr.transfer.NodeFinder#findFrom(org.alfresco.service.cmr.repository.NodeRef,
* org.alfresco.service.ServiceRegistry)
*/
public Set<NodeRef> findFrom(NodeRef thisNode)
{
if (!initialised)
{
init();
}
if (exclude)
{
return processExcludedSet(thisNode);
}
else
{
return processIncludedSet(thisNode);
}
}
/**
* @param thisNode
* @param serviceRegistry
* @return
*/
private Set<NodeRef> processExcludedSet(NodeRef thisNode)
{
Set<NodeRef> results = new HashSet<NodeRef>(89);
NodeService nodeService = serviceRegistry.getNodeService();
// Find all the child nodes (filtering as necessary).
List<ChildAssociationRef> children = nodeService.getChildAssocs(thisNode);
boolean filterChildren = !childAssociationTypes.isEmpty();
for (ChildAssociationRef child : children)
{
if (!filterChildren || !childAssociationTypes.contains(child.getTypeQName()))
{
results.add(child.getChildRef());
}
}
return results;
}
private Set<NodeRef> processIncludedSet(NodeRef startingNode)
{
NodeService nodeService = serviceRegistry.getNodeService();
Set<NodeRef> foundNodes = new HashSet<NodeRef>(89);
for (QName assocType : childAssociationTypes)
{
List<ChildAssociationRef> children = nodeService.getChildAssocs(startingNode, assocType,
RegexQNamePattern.MATCH_ALL);
for (ChildAssociationRef child : children)
{
foundNodes.add(child.getChildRef());
}
}
return foundNodes;
}
/**
* @param serviceRegistry
* the serviceRegistry to set
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
public void init()
{
ParameterCheck.mandatory("serviceRegistry", serviceRegistry);
// Quickly scan the supplied association types and remove any that either
// do not exist or are not child association types.
DictionaryService dictionaryService = serviceRegistry.getDictionaryService();
childAssociationTypes.clear();
for (QName associationType : suppliedAssociationTypes)
{
AssociationDefinition assocDef = dictionaryService.getAssociation(associationType);
if (assocDef != null && assocDef.isChild())
{
childAssociationTypes.add(associationType);
}
}
initialised = true;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.Set;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.transfer.TransferException;
/**
*
* @author Mark Rogers
*/
public interface ContentChunkProcessor
{
/**
* process this chunk of content data
* @param data
*/
public void processChunk(Set<ContentData> data) throws TransferException;
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.transfer.TransferException;
/**
* The Content Chunker Splits Content into "Chunks" of a given size
*
* @author Mark
*/
public interface ContentChunker
{
/**
* add content data to the chunker
*/
public void addContent(ContentData data) throws TransferException;
/**
* flush any remaining content data
*/
public void flush() throws TransferException;
/**
*
* @param chunkSize
*/
public void setChunkSize(long chunkSize);
/**
*
* @return
*/
public long getChunkSize();
/**
*
* @param handler
*/
public void setHandler(ContentChunkProcessor handler);
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.alfresco.repo.site.SiteServiceImpl;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.transfer.TransferException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The Content Chunker Splits Content into "Chunks" of a given size.
*
* @author Mark
*/
public class ContentChunkerImpl implements ContentChunker
{
private static Log logger = LogFactory.getLog(ContentChunkerImpl.class);
/**
* The chunk size.
*/
private long chunkSize = 1000000;
/**
* The handler to recieve the "chunks"
*/
private ContentChunkProcessor handler;
/**
* The internal buffer
*/
private Set<ContentData> buffer = new HashSet<ContentData>();
/**
*
*/
public void addContent(ContentData data) throws TransferException
{
logger.debug("add content size:" + data.getSize());
buffer.add(data);
/**
* work out whether the buffer has filled up and needs to be flushed
*/
Iterator iter = buffer.iterator();
long totalContentSize = 0;
while (iter.hasNext())
{
ContentData x = (ContentData)iter.next();
totalContentSize += x.getSize();
}
if(logger.isDebugEnabled())
{
logger.debug("elements " + buffer.size() + ", totalContentSize:" + totalContentSize);
}
if(totalContentSize >= chunkSize)
{
flush();
}
}
/**
*
*/
public void flush() throws TransferException
{
logger.debug("flush number of contents:" + buffer.size());
if(buffer.size() > 0)
{
handler.processChunk(buffer);
}
buffer.clear();
logger.debug("buffer empty");
}
public void setChunkSize(long chunkSize)
{
this.chunkSize = chunkSize;
}
public long getChunkSize()
{
return chunkSize;
}
public void setHandler(ContentChunkProcessor handler)
{
this.handler = handler;
}
public ContentChunkProcessor getHandler()
{
return handler;
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.HashSet;
import java.util.Set;
import org.alfresco.service.cmr.repository.ContentData;
import junit.framework.TestCase;
/**
* Unit test of the Content Chunker
*
* @author Mark Rogers
*/
public class ContentChunkerImplTest extends TestCase
{
public void testContentChunkerImpl() throws Exception
{
ContentChunker chunker = new ContentChunkerImpl();
final Set<ContentData>processedContent = new HashSet<ContentData>();
chunker.setHandler(
new ContentChunkProcessor(){
public void processChunk(Set<ContentData> data)
{
processedContent.addAll(data);
}
}
);
/**
* Set a chunk size of 10 bytes
*/
chunker.setChunkSize(10);
/**
* add one contet of size 20, should flush immediatly
*/
chunker.addContent(new ContentData(null, null, 20, null));
assertTrue("size 20 not written immediatley", processedContent.size() == 1);
/**
* add one content of size 1 - should remain buffered in chunker
*/
processedContent.clear();
chunker.addContent(new ContentData(null, null, 1, null));
assertTrue("size 1 not buffered", processedContent.size() == 0);
/**
* flush should write it out
*/
chunker.flush();
assertTrue("size 1 not flushed", processedContent.size() == 1);
/**
* Now test the boundary condition over the threashold
*/
processedContent.clear();
for(int i = 0; i < 11 ; i++)
{
chunker.addContent(new ContentData(null, null, 1, null));
}
assertEquals("size 10 not buffered", processedContent.size(), 10);
/**
* flush should write it out
*/
chunker.flush();
assertTrue("size 1 not flushed", processedContent.size() == 11);
/**
* Now Just whack some load through
*/
processedContent.clear();
for(int i = 0; i < 100 ; i++)
{
chunker.addContent(new ContentData(null, null, 3, null));
}
chunker.flush();
assertEquals("size 100 not written", processedContent.size(), 100);
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.transfer.NodeFilter;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ParameterCheck;
/**
* @author brian
*
*/
public class ContentClassFilter implements NodeFilter
{
private Set<QName> contentClasses = new HashSet<QName>();
private Set<QName> aspects = new HashSet<QName>();
private Set<QName> types = new HashSet<QName>();
private boolean initialised = false;
private boolean exclude = false;
private boolean directOnly = false;
private ServiceRegistry serviceRegistry;
public ContentClassFilter()
{
}
public ContentClassFilter(QName... contentClasses)
{
setContentClasses(contentClasses);
}
public ContentClassFilter(ServiceRegistry serviceRegistry)
{
this();
this.serviceRegistry = serviceRegistry;
}
public ContentClassFilter(ServiceRegistry serviceRegistry, QName... contentClasses)
{
this(serviceRegistry);
setContentClasses(contentClasses);
}
/*
* (non-Javadoc)
*
* @see org.alfresco.service.cmr.transfer.NodeFilter#accept(org.alfresco.service.cmr.repository.NodeRef,
* org.alfresco.service.ServiceRegistry)
*/
public boolean accept(NodeRef thisNode)
{
if (!initialised)
{
init();
}
NodeService nodeService = serviceRegistry.getNodeService();
Set<QName> nodesAspects = nodeService.getAspects(thisNode);
QName type = nodeService.getType(thisNode);
boolean typeIsInSet = types.contains(type);
boolean aspectIsInSet = false;
for (QName aspect : nodesAspects)
{
if (aspects.contains(aspect))
{
aspectIsInSet = true;
break;
}
}
return (!exclude && (typeIsInSet || aspectIsInSet)) || (exclude && (!typeIsInSet && !aspectIsInSet));
}
/**
* @param serviceRegistry
* the serviceRegistry to set
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
/**
* @param serviceRegistry
*/
public void init()
{
ParameterCheck.mandatory("serviceRegistry", serviceRegistry);
DictionaryService dictionaryService = serviceRegistry.getDictionaryService();
aspects.clear();
types.clear();
for (QName contentClass : contentClasses)
{
ClassDefinition classDef = dictionaryService.getClass(contentClass);
if (classDef == null)
{
continue;
}
if (classDef.isAspect())
{
aspects.add(contentClass);
if (!directOnly)
{
aspects.addAll(dictionaryService.getSubAspects(contentClass, true));
}
}
else
{
types.add(contentClass);
if (!directOnly)
{
types.addAll(dictionaryService.getSubTypes(contentClass, true));
}
}
}
initialised = true;
}
/**
* Set the classes of content (types and aspects) to filter by.
*
* @param contentClasses
* the contentClasses to set
*/
public void setContentClasses(Collection<QName> contentClasses)
{
this.contentClasses = new HashSet<QName>(contentClasses);
initialised = false;
}
/**
* Set the classes of content (types and aspects) to filter by.
*
* @param contentClasses
* the contentClasses to set
*/
public void setContentClasses(QName... contentClasses)
{
this.contentClasses = new HashSet<QName>(Arrays.asList(contentClasses));
initialised = false;
}
/**
* Specify whether the filter should exclude the specified classes of content.
*
* @param exclude
* If true then this filter will not accept content that is of any of the filtered classes of content. If
* false then this filter will only accept content that has one or more of the filtered classes of
* content. Defaults to false.
*/
public void setExclude(boolean exclude)
{
this.exclude = exclude;
}
/**
* Specify whether the filter should only test against the content classes that have been supplied, or if it should
* also test against all subclasses of those classes. For example, if "directOnly" is true and "cm:content" is in
* the set of content classes then a node of type "cm:thumnail" will not be filtered.
*
* @param directOnly
* If true then the filter only filters specifically the specified content classes. Defaults to false.
*/
public void setDirectOnly(boolean directOnly)
{
this.directOnly = directOnly;
initialised = false;
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.apache.commons.httpclient.methods.multipart.PartBase;
import org.apache.commons.httpclient.util.EncodingUtil;
/**
* @author brian
*
*/
public class ContentDataPart extends PartBase
{
/** Attachment's file name */
protected static final String FILE_NAME = "; filename=";
/** Attachment's file name as a byte array */
private static final byte[] FILE_NAME_BYTES =
EncodingUtil.getAsciiBytes(FILE_NAME);
private ContentService contentService;
private ContentData data;
private String filename;
/**
* ContentDataPart
* @param contentService
* @param partName
* @param data
*/
public ContentDataPart(ContentService contentService, String partName, ContentData data) {
super(partName, data.getMimetype(), data.getEncoding(), null);
this.contentService = contentService;
this.data = data;
this.filename = partName;
}
/**
* Write the disposition header to the output stream
* @param out The output stream
* @throws IOException If an IO problem occurs
* @see Part#sendDispositionHeader(OutputStream)
*/
protected void sendDispositionHeader(OutputStream out)
throws IOException {
super.sendDispositionHeader(out);
if (filename != null) {
out.write(FILE_NAME_BYTES);
out.write(QUOTE_BYTES);
out.write(EncodingUtil.getAsciiBytes(filename));
out.write(QUOTE_BYTES);
}
}
/* (non-Javadoc)
* @see org.apache.commons.httpclient.methods.multipart.Part#lengthOfData()
*/
@Override
protected long lengthOfData() throws IOException
{
return data.getSize();
}
/* (non-Javadoc)
* @see org.apache.commons.httpclient.methods.multipart.Part#sendData(java.io.OutputStream)
*/
@Override
protected void sendData(OutputStream out) throws IOException
{
// Get the content from the content URL and write it to out
// Can't use the following code since it closes 'out', which needs to remain open.
// AbstractContentReader.
// contentService.getRawReader(data.getContentUrl()).getContent(out);
InputStream is = contentService.getRawReader(data.getContentUrl()).getContentInputStream();
// ReadableByteChannel rbc = Channels.newChannel(is);
// WritableByteChannel wbc = Channels.newChannel(out);
try
{
byte[] tmp = new byte[4096];
int len;
while ((len = is.read(tmp)) >= 0)
{
out.write(tmp, 0, len);
}
}
finally
{
// we're done with the input stream, close it
is.close();
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Path;
/**
* @author brian
*
*/
public interface CorrespondingNodeResolver
{
ResolvedParentChildPair resolveCorrespondingNode(NodeRef sourceNodeRef, ChildAssociationRef primaryAssoc,
Path parentPath);
static class ResolvedParentChildPair {
public NodeRef resolvedParent;
public NodeRef resolvedChild;
public ResolvedParentChildPair(NodeRef parent, NodeRef child) {
this.resolvedParent = parent;
this.resolvedChild = child;
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
/**
* @author brian
*
*/
public interface CorrespondingNodeResolverFactory
{
/**
* @return
*/
CorrespondingNodeResolver getResolver();
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import org.alfresco.service.cmr.repository.NodeService;
/**
* @author brian
*
*/
public class DefaultCorrespondingNodeResolverFactory implements CorrespondingNodeResolverFactory
{
private NodeService nodeService;
/* (non-Javadoc)
* @see org.alfresco.repo.transfer.CorrespondingNodeResolverFactory#getResolver()
*/
public CorrespondingNodeResolver getResolver()
{
BasicCorrespondingNodeResolverImpl basicResolver = new BasicCorrespondingNodeResolverImpl();
basicResolver.setNodeService(nodeService);
CachingCorrespondingNodeResolverImpl cachingResolver = new CachingCorrespondingNodeResolverImpl(basicResolver);
return cachingResolver;
}
/**
* @param nodeService the nodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.repo.transfer.manifest.TransferManifestProcessor;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.transfer.TransferReceiver;
/**
* @author brian
*
*/
public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFactory
{
private NodeService nodeService;
private ContentService contentService;
private CorrespondingNodeResolverFactory nodeResolverFactory;
/*
* (non-Javadoc)
*
* @see org.alfresco.repo.transfer.ManifestProcessorFactory#getPrimaryCommitProcessor()
*/
public List<TransferManifestProcessor> getCommitProcessors(TransferReceiver receiver, String transferId)
{
List<TransferManifestProcessor> processors = new ArrayList<TransferManifestProcessor>();
CorrespondingNodeResolver nodeResolver = nodeResolverFactory.getResolver();
RepoPrimaryManifestProcessorImpl primaryProcessor = new RepoPrimaryManifestProcessorImpl(receiver, transferId);
primaryProcessor.setContentService(contentService);
primaryProcessor.setNodeResolver(nodeResolver);
primaryProcessor.setNodeService(nodeService);
processors.add(primaryProcessor);
RepoSecondaryManifestProcessorImpl secondaryProcessor = new RepoSecondaryManifestProcessorImpl(transferId);
secondaryProcessor.setNodeResolver(nodeResolver);
secondaryProcessor.setNodeService(nodeService);
processors.add(secondaryProcessor);
return processors;
}
/**
* @param nodeService the nodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param contentService the contentService to set
*/
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
/**
* @param nodeResolverFactory the nodeResolverFactory to set
*/
public void setNodeResolverFactory(CorrespondingNodeResolverFactory nodeResolverFactory)
{
this.nodeResolverFactory = nodeResolverFactory;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
/**
* Details back from the manifest to say which nodes the remote server already has.
*
* @author Mark Rogers
*/
public class DeltaList
{
}

View File

@@ -0,0 +1,569 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.io.File;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.transfer.TransferException;
import org.alfresco.service.cmr.transfer.TransferTarget;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.httpclient.protocol.SSLProtocolSocketFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* HTTP implementation of TransferTransmitter.
*
* Sends data via HTTP to the server.
*
* @author brian
*/
public class HttpClientTransmitterImpl implements TransferTransmitter
{
private static final Log log = LogFactory.getLog(HttpClientTransmitterImpl.class);
private static final String MSG_UNSUPPORTED_PROTOCOL = "transfer_service.comms.unsupported_protocol";
private static final String MSG_UNSUCCESSFUL_RESPONSE = "transfer_service.comms.unsuccessful_response";
private static final String MSG_HTTP_REQUEST_FAILED = "transfer_service.comms.http_request_failed";
private static final int DEFAULT_HTTP_PORT = 80;
private static final int DEFAULT_HTTPS_PORT = 443;
private static final String HTTP_SCHEME_NAME = "http"; // lowercase is important
private static final String HTTPS_SCHEME_NAME = "https"; // lowercase is important
private HttpClient httpClient = null;
private Protocol httpProtocol = new Protocol(HTTP_SCHEME_NAME, new DefaultProtocolSocketFactory(), DEFAULT_HTTP_PORT);
private Protocol httpsProtocol = new Protocol(HTTPS_SCHEME_NAME, (ProtocolSocketFactory) new SSLProtocolSocketFactory(), DEFAULT_HTTPS_PORT);
private Map<String,Protocol> protocolMap = null;
private ContentService contentService;
public HttpClientTransmitterImpl()
{
protocolMap = new TreeMap<String,Protocol>();
protocolMap.put(HTTP_SCHEME_NAME, httpProtocol);
protocolMap.put(HTTPS_SCHEME_NAME, httpsProtocol);
httpClient = new HttpClient();
httpClient.setHttpConnectionManager(new MultiThreadedHttpConnectionManager());
}
public void init()
{
}
/**
* By default this class uses the standard SSLProtocolSocketFactory, but this method allows this to be overridden.
* Useful if, for example, one wishes to permit support of self-signed certificates on the target.
* @param socketFactory
*/
public void setHttpsSocketFactory(ProtocolSocketFactory socketFactory)
{
protocolMap.put(HTTPS_SCHEME_NAME, new Protocol(HTTPS_SCHEME_NAME, socketFactory, DEFAULT_HTTPS_PORT));
}
/**
* By default, this class uses a plain HttpClient instance with the only non-default
* option being the multi-threaded connection manager.
* Use this method to replace this with your own HttpClient instance configured how you wish
* @param httpClient
*/
public void setHttpClient(HttpClient httpClient)
{
this.httpClient = httpClient;
}
/* (non-Javadoc)
* @see org.alfresco.repo.transfer.Transmitter#verifyTarget(org.alfresco.service.cmr.transfer.TransferTarget)
*/
public void verifyTarget(TransferTarget target) throws TransferException
{
HttpMethod verifyRequest = new PostMethod();
try
{
HostConfiguration hostConfig = getHostConfig(target);
HttpState httpState = getHttpState(target);
verifyRequest.setPath(target.getEndpointPath() + "/test");
try
{
int response = httpClient.executeMethod(hostConfig, verifyRequest, httpState);
checkResponseStatus("verifyTarget", response, verifyRequest);
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
String error = "Failed to execute HTTP request to target";
log.debug(error, e);
throw new TransferException(MSG_HTTP_REQUEST_FAILED, new Object[]{"verifyTraget", target.toString(), e.toString()}, e);
}
}
finally
{
verifyRequest.releaseConnection();
}
}
/**
* @param response
*/
private void checkResponseStatus(String methodName, int response, HttpMethod method)
{
if (response != 200)
{
String errorId = null;
String[] errorParams = null;
try {
log.error("Received \"unsuccessful\" response code from target server: " + response);
String errorPayload = method.getResponseBodyAsString();
JSONObject errorObj = new JSONObject(errorPayload);
errorId = errorObj.getString("errorId");
JSONArray errorParamArray = errorObj.getJSONArray("errorParams");
int length = errorParamArray.length();
errorParams = new String[length];
for (int i = 0; i < length; ++i)
{
errorParams[i] = errorParamArray.getString(i);
}
} catch (Exception ex) {
throw new TransferException(MSG_UNSUCCESSFUL_RESPONSE, new Object[] {methodName, response});
}
throw new TransferException(errorId, errorParams);
}
}
/**
* Get the HTTPState for a transfer target
* @param target
* @return
*/
private HttpState getHttpState(TransferTarget target)
{
HttpState httpState = new HttpState();
httpState.setCredentials(new AuthScope(target.getEndpointHost(), target.getEndpointPort(),
AuthScope.ANY_REALM),
new UsernamePasswordCredentials(target.getUsername(), new String(target.getUsername())));
return httpState;
}
/**
* @param target
* @return
*/
private HostConfiguration getHostConfig(TransferTarget target)
{
String requiredProtocol = target.getEndpointProtocol();
if (requiredProtocol == null)
{
throw new TransferException(MSG_UNSUPPORTED_PROTOCOL, new Object[] {target.getEndpointProtocol()});
}
Protocol protocol = protocolMap.get(requiredProtocol.toLowerCase().trim());
if (protocol == null) {
log.error("Unsupported protocol: " + target.getEndpointProtocol());
throw new TransferException(MSG_UNSUPPORTED_PROTOCOL, new Object[] {target.getEndpointProtocol()});
}
HostConfiguration hostConfig = new HostConfiguration();
hostConfig.setHost(target.getEndpointHost(), target.getEndpointPort(), protocol);
return hostConfig;
}
public Transfer begin(TransferTarget target) throws TransferException
{
HttpMethod beginRequest = new PostMethod();
try
{
HostConfiguration hostConfig = getHostConfig(target);
HttpState httpState = getHttpState(target);
beginRequest.setPath(target.getEndpointPath() + "/begin");
try
{
int responseStatus = httpClient.executeMethod(hostConfig, beginRequest, httpState);
checkResponseStatus("begin", responseStatus, beginRequest);
//If we get here then we've received a 200 response
//We're expecting the transfer id encoded in a JSON object...
JSONObject response = new JSONObject(beginRequest.getResponseBodyAsString());
String transferId = response.getString("transferId");
if(log.isDebugEnabled())
{
log.debug("begin transfer transferId:" + transferId +", target:" + target);
}
Transfer transfer = new Transfer();
transfer.setTransferId(transferId);
transfer.setTransferTarget(target);
return transfer;
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
String error = "Failed to execute HTTP request to target";
log.debug(error, e);
throw new TransferException(MSG_HTTP_REQUEST_FAILED, new Object[] {"begin", target.toString(), e.toString()}, e);
}
}
finally
{
beginRequest.releaseConnection();
}
}
public DeltaList sendManifest(Transfer transfer, File manifest) throws TransferException
{
TransferTarget target = transfer.getTransferTarget();
PostMethod postSnapshotRequest = new PostMethod();
MultipartRequestEntity requestEntity;
if(log.isDebugEnabled())
{
log.debug("does manifest exist? " + manifest.exists());
log.debug("sendManifest file : " + manifest.getAbsoluteFile());
}
try
{
HostConfiguration hostConfig = getHostConfig(target);
HttpState httpState = getHttpState(target);
try
{
postSnapshotRequest.setPath(target.getEndpointPath() + "/post-snapshot");
//Put the transferId on the query string
postSnapshotRequest.setQueryString(
new NameValuePair[] {new NameValuePair("transferId", transfer.getTransferId())});
//TODO encapsulate the name of the manifest part
//And add the manifest file as a "part"
Part file = new FilePart(TransferCommons.PART_NAME_MANIFEST, manifest);
requestEntity = new MultipartRequestEntity(new Part[] {file}, postSnapshotRequest.getParams());
postSnapshotRequest.setRequestEntity(requestEntity);
int responseStatus = httpClient.executeMethod(hostConfig, postSnapshotRequest, httpState);
checkResponseStatus("sendManifest", responseStatus, postSnapshotRequest);
return null;
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
String error = "Failed to execute HTTP request to target";
log.debug(error, e);
throw new TransferException(MSG_HTTP_REQUEST_FAILED, new Object[]{"sendManifest", target.toString(), e.toString()}, e);
}
}
finally
{
postSnapshotRequest.releaseConnection();
}
}
public void abort(Transfer transfer) throws TransferException
{
TransferTarget target = transfer.getTransferTarget();
HttpMethod abortRequest = new PostMethod();
try
{
HostConfiguration hostConfig = getHostConfig(target);
HttpState httpState = getHttpState(target);
abortRequest.setPath(target.getEndpointPath() + "/abort");
//Put the transferId on the query string
abortRequest.setQueryString(
new NameValuePair[] {new NameValuePair("transferId", transfer.getTransferId())});
try
{
int responseStatus = httpClient.executeMethod(hostConfig, abortRequest, httpState);
checkResponseStatus("abort", responseStatus, abortRequest);
//If we get here then we've received a 200 response
//We're expecting the transfer id encoded in a JSON object...
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
String error = "Failed to execute HTTP request to target";
log.debug(error, e);
throw new TransferException(MSG_HTTP_REQUEST_FAILED, new Object[]{"abort", target.toString(), e.toString()}, e);
}
}
finally
{
abortRequest.releaseConnection();
}
}
public void commit(Transfer transfer) throws TransferException
{
TransferTarget target = transfer.getTransferTarget();
HttpMethod commitRequest = new PostMethod();
try
{
HostConfiguration hostConfig = getHostConfig(target);
HttpState httpState = getHttpState(target);
commitRequest.setPath(target.getEndpointPath() + "/commit");
//Put the transferId on the query string
commitRequest.setQueryString(
new NameValuePair[] {new NameValuePair("transferId", transfer.getTransferId())});
try
{
int responseStatus = httpClient.executeMethod(hostConfig, commitRequest, httpState);
checkResponseStatus("commit", responseStatus, commitRequest);
//If we get here then we've received a 200 response
//We're expecting the transfer id encoded in a JSON object...
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
String error = "Failed to execute HTTP request to target";
log.error(error, e);
throw new TransferException(MSG_HTTP_REQUEST_FAILED, new Object[]{"commit", target.toString(), e.toString()}, e);
}
}
finally
{
commitRequest.releaseConnection();
}
}
public void prepare(Transfer transfer) throws TransferException
{
TransferTarget target = transfer.getTransferTarget();
HttpMethod prepareRequest = new PostMethod();
try
{
HostConfiguration hostConfig = getHostConfig(target);
HttpState httpState = getHttpState(target);
prepareRequest.setPath(target.getEndpointPath() + "/prepare");
//Put the transferId on the query string
prepareRequest.setQueryString(
new NameValuePair[] {new NameValuePair("transferId", transfer.getTransferId())});
try
{
int responseStatus = httpClient.executeMethod(hostConfig, prepareRequest, httpState);
checkResponseStatus("prepare", responseStatus, prepareRequest);
//If we get here then we've received a 200 response
//We're expecting the transfer id encoded in a JSON object...
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
String error = "Failed to execute HTTP request to target";
log.debug(error, e);
throw new TransferException(MSG_HTTP_REQUEST_FAILED, new Object[]{"prepare", target.toString(), e.toString()}, e);
}
}
finally
{
prepareRequest.releaseConnection();
}
}
/**
*
*/
public void sendContent(Transfer transfer, Set<ContentData> data) throws TransferException
{
if(log.isDebugEnabled())
{
log.debug("send content to transfer:" + transfer);
}
TransferTarget target = transfer.getTransferTarget();
PostMethod postContentRequest = new PostMethod();
try
{
HostConfiguration hostConfig = getHostConfig(target);
HttpState httpState = getHttpState(target);
try
{
postContentRequest.setPath(target.getEndpointPath() + "/post-content");
//Put the transferId on the query string
postContentRequest.setQueryString(
new NameValuePair[] {new NameValuePair("transferId", transfer.getTransferId())});
//Put the transferId on the query string
postContentRequest.setQueryString(
new NameValuePair[] {new NameValuePair("transferId", transfer.getTransferId())});
Part[] parts = new Part[data.size()];
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);
parts[index++] = new ContentDataPart(getContentService(), fileName, content);
}
MultipartRequestEntity requestEntity = new MultipartRequestEntity(parts, postContentRequest.getParams());
postContentRequest.setRequestEntity(requestEntity);
int responseStatus = httpClient.executeMethod(hostConfig, postContentRequest, httpState);
checkResponseStatus("sendContent", responseStatus, postContentRequest);
if(log.isDebugEnabled())
{
log.debug("sent content");
}
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
String error = "Failed to execute HTTP request to target";
log.debug(error, e);
throw new TransferException(MSG_HTTP_REQUEST_FAILED, new Object[]{"sendContent", target.toString(), e.toString()}, e);
}
}
finally
{
postContentRequest.releaseConnection();
}
} // end of sendContent
/**
*
*/
public Set<TransferMessage> getMessages(Transfer transfer)
{
// TODO How to signal last message ? return null rather than empty
Set<TransferMessage> messages = new HashSet<TransferMessage>();
TransferTarget target = transfer.getTransferTarget();
HttpMethod messagesRequest = new GetMethod();
try
{
HostConfiguration hostConfig = getHostConfig(target);
HttpState httpState = getHttpState(target);
messagesRequest.setPath(target.getEndpointPath() + "/messages");
//Put the transferId on the query string
messagesRequest.setQueryString(
new NameValuePair[] {new NameValuePair("transferId", transfer.getTransferId())});
try
{
int responseStatus = httpClient.executeMethod(hostConfig, messagesRequest, httpState);
checkResponseStatus("getMessages", responseStatus, messagesRequest);
//If we get here then we've received a 200 response
//We're expecting the transfer id encoded in a JSON object...
JSONObject lookupResult = new JSONObject(messagesRequest.getResponseBodyAsString());
JSONArray data = lookupResult.getJSONArray("data");
for(int i = 0; i < data.length(); i++)
{
JSONObject obj = data.getJSONObject(i);
String message = obj.getString("message");
//TODO Need some sort of TransferMessage impl
// messages.add(new TransferMessageImpl());
}
return messages;
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
String error = "Failed to execute HTTP request to target";
log.debug(error, e);
throw new TransferException(MSG_HTTP_REQUEST_FAILED, new Object[]{"getMessages", target.toString(), e.toString()}, e);
}
}
finally
{
messagesRequest.releaseConnection();
}
}
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
public ContentService getContentService()
{
return contentService;
}
} // end of class

View File

@@ -0,0 +1,247 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import junit.framework.TestCase;
import org.alfresco.service.cmr.transfer.TransferException;
import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.mockito.ArgumentCaptor;
/**
* Unit test for HttpClientTransmitterImpl
*
* @author Brian Remmington
*/
public class HttpClientTransmitterImplTest extends TestCase
{
private static final String TARGET_HOST = "my.testhost.com";
private static final String HTTP_PROTOCOL = "HTTP";
private static final String HTTPS_PROTOCOL = "HTTPS";
private static final String TRANSFER_SERVICE_PATH = "/api/transfer";
private static final int HTTP_PORT = 80;
private static final int HTTPS_PORT = 443;
private static final String TARGET_USERNAME = "transferuser";
private static final char[] TARGET_PASSWORD = "password".toCharArray();
private HttpClientTransmitterImpl transmitter;
private HttpClient mockedHttpClient;
private TransferTargetImpl target;
/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
@Override
protected void setUp() throws Exception
{
super.setUp();
this.transmitter = new HttpClientTransmitterImpl();
this.mockedHttpClient = mock(HttpClient.class);
transmitter.setHttpClient(mockedHttpClient);
this.target = new TransferTargetImpl();
target.setEndpointHost(TARGET_HOST);
target.setEndpointProtocol(HTTP_PROTOCOL);
target.setEndpointPath(TRANSFER_SERVICE_PATH);
target.setEndpointPort(HTTP_PORT);
target.setUsername(TARGET_USERNAME);
target.setPassword(TARGET_PASSWORD);
}
/**
* Test create target.
*
* @throws Exception
*/
public void testSuccessfulVerifyTargetOverHttp() throws Exception
{
//Stub HttpClient so that executeMethod returns a 200 response
when(mockedHttpClient.executeMethod(any(HostConfiguration.class), any(HttpMethod.class),
any(HttpState.class))).thenReturn(200);
//Call verifyTarget
transmitter.verifyTarget(target);
ArgumentCaptor<HostConfiguration> hostConfig = ArgumentCaptor.forClass(HostConfiguration.class);
ArgumentCaptor<HttpMethod> httpMethod = ArgumentCaptor.forClass(HttpMethod.class);
ArgumentCaptor<HttpState> httpState = ArgumentCaptor.forClass(HttpState.class);
verify(mockedHttpClient).executeMethod(hostConfig.capture(), httpMethod.capture(), httpState.capture());
assertTrue("post method", httpMethod.getValue() instanceof PostMethod);
assertEquals("host name", TARGET_HOST, hostConfig.getValue().getHost());
assertEquals("port", HTTP_PORT, hostConfig.getValue().getPort());
assertEquals("protocol", HTTP_PROTOCOL.toLowerCase(),
hostConfig.getValue().getProtocol().getScheme().toLowerCase());
assertEquals("path", TRANSFER_SERVICE_PATH + "/test", httpMethod.getValue().getPath());
}
public void testSuccessfulVerifyTargetOverHttps() throws Exception
{
//Stub HttpClient so that executeMethod returns a 200 response
when(mockedHttpClient.executeMethod(any(HostConfiguration.class), any(HttpMethod.class),
any(HttpState.class))).thenReturn(200);
target.setEndpointProtocol(HTTPS_PROTOCOL);
target.setEndpointPort(HTTPS_PORT);
//Call verifyTarget
transmitter.verifyTarget(target);
ArgumentCaptor<HostConfiguration> hostConfig = ArgumentCaptor.forClass(HostConfiguration.class);
ArgumentCaptor<HttpMethod> httpMethod = ArgumentCaptor.forClass(HttpMethod.class);
ArgumentCaptor<HttpState> httpState = ArgumentCaptor.forClass(HttpState.class);
verify(mockedHttpClient).executeMethod(hostConfig.capture(), httpMethod.capture(), httpState.capture());
assertEquals("port", HTTPS_PORT, hostConfig.getValue().getPort());
assertTrue("socket factory",
hostConfig.getValue().getProtocol().getSocketFactory() instanceof SecureProtocolSocketFactory);
assertEquals("protocol", HTTPS_PROTOCOL.toLowerCase(),
hostConfig.getValue().getProtocol().getScheme().toLowerCase());
}
public void testHttpsVerifyTargetWithCustomSocketFactory() throws Exception
{
//Override the default SSL socket factory with our own custom one...
CustomSocketFactory socketFactory = new CustomSocketFactory();
transmitter.setHttpsSocketFactory(socketFactory);
target.setEndpointProtocol(HTTPS_PROTOCOL);
target.setEndpointPort(HTTPS_PORT);
//Stub HttpClient so that executeMethod returns a 200 response
when(mockedHttpClient.executeMethod(any(HostConfiguration.class), any(HttpMethod.class),
any(HttpState.class))).thenReturn(200);
//Call verifyTarget
transmitter.verifyTarget(target);
ArgumentCaptor<HostConfiguration> hostConfig = ArgumentCaptor.forClass(HostConfiguration.class);
ArgumentCaptor<HttpMethod> httpMethod = ArgumentCaptor.forClass(HttpMethod.class);
ArgumentCaptor<HttpState> httpState = ArgumentCaptor.forClass(HttpState.class);
verify(mockedHttpClient).executeMethod(hostConfig.capture(), httpMethod.capture(), httpState.capture());
assertEquals("port", HTTPS_PORT, hostConfig.getValue().getPort());
//test that the socket factory passed to HttpClient is our custom one (intentional use of '==')
assertTrue("socket factory", hostConfig.getValue().getProtocol().getSocketFactory() == socketFactory);
assertEquals("protocol", HTTPS_PROTOCOL.toLowerCase(),
hostConfig.getValue().getProtocol().getScheme().toLowerCase());
}
public void testVerifyTargetWithInvalidProtocol() throws Exception
{
target.setEndpointProtocol("invalidprotocol");
try
{
transmitter.verifyTarget(target);
fail("invalid protocol");
}
catch(TransferException ex)
{
//expected
}
}
public void testUnauthorisedVerifyTarget() throws Exception
{
//Stub HttpClient so that executeMethod returns a 401 response
when(mockedHttpClient.executeMethod(any(HostConfiguration.class), any(HttpMethod.class),
any(HttpState.class))).thenReturn(401);
try
{
transmitter.verifyTarget(target);
}
catch (TransferException ex)
{
//expected
}
}
private static class CustomSocketFactory implements SecureProtocolSocketFactory
{
/* (non-Javadoc)
* @see org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory#createSocket(java.net.Socket, java.lang.String, int, boolean)
*/
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,
UnknownHostException
{
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see org.apache.commons.httpclient.protocol.ProtocolSocketFactory#createSocket(java.lang.String, int)
*/
public Socket createSocket(String host, int port) throws IOException, UnknownHostException
{
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see org.apache.commons.httpclient.protocol.ProtocolSocketFactory#createSocket(java.lang.String, int, java.net.InetAddress, int)
*/
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException,
UnknownHostException
{
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see org.apache.commons.httpclient.protocol.ProtocolSocketFactory#createSocket(java.lang.String, int, java.net.InetAddress, int, org.apache.commons.httpclient.params.HttpConnectionParams)
*/
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort,
HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException
{
// TODO Auto-generated method stub
return null;
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.List;
import org.alfresco.repo.transfer.manifest.TransferManifestProcessor;
import org.alfresco.service.cmr.transfer.TransferReceiver;
/**
* @author brian
*
*/
public interface ManifestProcessorFactory
{
List<TransferManifestProcessor> getCommitProcessors(TransferReceiver receiver, String transferId);
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
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.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseAlfrescoSpringTest;
import org.alfresco.util.GUID;
/**
* Unit test for RepoTransferReceiverImpl
*
* @author Brian Remmington
*/
public class NodeCrawlerTest extends BaseAlfrescoSpringTest
{
private ServiceRegistry serviceRegistry;
private NodeRef companyHome;
/**
* Called during the transaction setup
*/
@SuppressWarnings("deprecation")
protected void onSetUpInTransaction() throws Exception
{
super.onSetUpInTransaction();
// Get the required services
this.nodeService = (NodeService) this.getApplicationContext().getBean("NodeService");
this.serviceRegistry = (ServiceRegistry) this.getApplicationContext().getBean("ServiceRegistry");
ResultSet rs = serviceRegistry.getSearchService().query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
SearchService.LANGUAGE_XPATH, "/app:company_home");
if (rs.length() == 0)
{
fail("Could not find company home");
}
companyHome = rs.getNodeRef(0);
}
public void testContentClassFilter() throws Exception
{
NodeRef node1 = makeNode(companyHome, ContentModel.TYPE_BASE);
NodeRef node2 = makeNode(companyHome, ContentModel.TYPE_CONTENT);
NodeRef node3 = makeNode(companyHome, ContentModel.TYPE_FOLDER);
NodeRef node4 = makeNode(companyHome, ContentModel.TYPE_CONTENT);
NodeRef node5 = makeNode(companyHome, ContentModel.TYPE_THUMBNAIL);
nodeService.addAspect(node4, ContentModel.ASPECT_REFERENCEABLE, null);
ContentClassFilter contentFilter = new ContentClassFilter(serviceRegistry);
contentFilter.setContentClasses(ContentModel.TYPE_BASE);
assertTrue(contentFilter.accept(node1));
assertTrue(contentFilter.accept(node2));
assertTrue(contentFilter.accept(node3));
contentFilter.setDirectOnly(true);
assertTrue(contentFilter.accept(node1));
assertFalse(contentFilter.accept(node2));
assertFalse(contentFilter.accept(node3));
contentFilter.setContentClasses(ContentModel.TYPE_BASE, ContentModel.ASPECT_REFERENCEABLE);
assertTrue(contentFilter.accept(node4));
contentFilter.setExclude(true);
contentFilter.setDirectOnly(false);
contentFilter.setContentClasses(ContentModel.TYPE_CONTENT);
assertTrue(contentFilter.accept(node1));
assertFalse(contentFilter.accept(node2));
assertTrue(contentFilter.accept(node3));
assertFalse(contentFilter.accept(node5));
contentFilter.setDirectOnly(true);
assertTrue(contentFilter.accept(node1));
assertFalse(contentFilter.accept(node2));
assertTrue(contentFilter.accept(node3));
assertTrue(contentFilter.accept(node5));
}
public void testChildAssociationFinder()
{
makeNode(companyHome, ContentModel.TYPE_BASE);
makeNode(companyHome, ContentModel.TYPE_CONTENT);
makeNode(companyHome, ContentModel.TYPE_CONTENT);
makeNode(companyHome, ContentModel.TYPE_CONTENT);
makeNode(companyHome, ContentModel.TYPE_CONTENT);
makeNode(companyHome, ContentModel.TYPE_CONTENT);
makeNode(companyHome, ContentModel.TYPE_CONTENT);
NodeRef node8 = makeNode(companyHome, ContentModel.TYPE_FOLDER);
makeNode(node8, ContentModel.TYPE_FOLDER);
NodeRef node10 = makeNode(node8, ContentModel.TYPE_FOLDER);
makeNode(node10, ContentModel.TYPE_FOLDER);
NodeRef node12 = makeNode(node10, ContentModel.TYPE_FOLDER);
NodeRef node13 = makeNode(node12, ContentModel.TYPE_FOLDER);
makeNode(node13, ContentModel.TYPE_CONTENT);
NodeRef node15 = makeNode(node13, ContentModel.TYPE_THUMBNAIL);
nodeService.addAspect(node8, ContentModel.ASPECT_THUMBNAILED, null);
nodeService.addChild(node8, node15, ContentModel.ASSOC_THUMBNAILS, QName.createQName(
NamespaceService.APP_MODEL_1_0_URI, "temp"));
ChildAssociatedNodeFinder nodeFinder = new ChildAssociatedNodeFinder(serviceRegistry);
nodeFinder.setAssociationTypes(ContentModel.ASSOC_CONTAINS);
Set<NodeRef> results = nodeFinder.findFrom(node8);
assertEquals(2, results.size());
nodeFinder.setAssociationTypes(ContentModel.ASSOC_THUMBNAILS);
results = nodeFinder.findFrom(node8);
assertEquals(1, results.size());
assertEquals(node15, new ArrayList<NodeRef>(results).get(0));
}
public void testCrawler()
{
NodeRef node8 = makeNode(companyHome, ContentModel.TYPE_FOLDER);
NodeRef node9 = makeNode(node8, ContentModel.TYPE_FOLDER);
NodeRef node10 = makeNode(node8, ContentModel.TYPE_FOLDER);
NodeRef node11 = makeNode(node10, ContentModel.TYPE_FOLDER);
NodeRef node12 = makeNode(node10, ContentModel.TYPE_FOLDER);
NodeRef node13 = makeNode(node12, ContentModel.TYPE_FOLDER);
makeNode(node10, ContentModel.TYPE_BASE);
makeNode(node13, ContentModel.TYPE_CONTENT);
makeNode(node10, ContentModel.TYPE_CONTENT);
makeNode(node11, ContentModel.TYPE_CONTENT);
makeNode(node8, ContentModel.TYPE_CONTENT);
makeNode(node8, ContentModel.TYPE_CONTENT);
makeNode(node9, ContentModel.TYPE_CONTENT);
makeNode(node13, ContentModel.TYPE_CONTENT);
NodeRef node15 = makeNode(node13, ContentModel.TYPE_THUMBNAIL);
nodeService.addAspect(node8, ContentModel.ASPECT_THUMBNAILED, null);
nodeService.addChild(node8, node15, ContentModel.ASSOC_THUMBNAILS, QName.createQName(
NamespaceService.APP_MODEL_1_0_URI, "temp"));
StandardNodeCrawlerImpl crawler = new StandardNodeCrawlerImpl(serviceRegistry);
crawler.setNodeFinders(new ChildAssociatedNodeFinder(ContentModel.ASSOC_CONTAINS));
Set<NodeRef> crawledNodes = crawler.crawl(node8);
assertEquals(15, crawledNodes.size());
crawler.setNodeFilters(new ContentClassFilter(ContentModel.TYPE_FOLDER));
crawledNodes = crawler.crawl(node8);
assertEquals(6, crawledNodes.size());
}
/**
* @param companyHome2
* @param nodeType
* @return
*/
private NodeRef makeNode(NodeRef parent, QName nodeType)
{
String uuid = GUID.generate();
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NAME, uuid);
ChildAssociationRef assoc = nodeService.createNode(parent, ContentModel.ASSOC_CONTAINS, QName.createQName(
NamespaceService.APP_MODEL_1_0_URI, uuid), nodeType, props);
return assoc.getChildRef();
}
}

View File

@@ -0,0 +1,61 @@
package org.alfresco.repo.transfer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.util.ISO9075;
public class PathHelper
{
/**
* Converts a String representation of a path to a Path
*
* e.g "/{http://www.alfresco.org/model/application/1.0}company_home/{http://www.alfresco.org/model/application/1.0}dictionary/{http://www.alfresco.org/model/application/1.0}transfers/{http://www.alfresco.org/model/content/1.0}default/{http://www.alfresco.org/model/transfer/1.0}snapshotMe";
* @param value the string representation of the path.
* @return Path
*/
public static Path stringToPath(String value)
{
Path path = new Path();
// pattern for QName e.g. /{stuff}stuff
Pattern pattern = Pattern.compile("/\\{[a-zA-Z:./0-9]*\\}[A-Z0-9a-z_ ]*");
Matcher matcher = pattern.matcher(value);
// This is the root node
path.append(new SimplePathElement("/"));
while ( matcher.find() )
{
String group = matcher.group();
final String val = ISO9075.decode(group.substring(1));
path.append(new SimplePathElement(val));
}
return path;
}
private static class SimplePathElement extends Path.Element {
private static final long serialVersionUID = -5243552616345217924L;
private String elementString;
public SimplePathElement(String elementString)
{
this.elementString = elementString;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.repository.Path.Element#getElementString()
*/
@Override
public String getElementString()
{
return elementString;
}
}
}

View File

@@ -0,0 +1,208 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.transfer.NodeFinder;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.ParameterCheck;
/**
* @author brian
*
*/
public class PeerAssociatedNodeFinder implements NodeFinder
{
private Set<QName> suppliedAssociationTypes = new HashSet<QName>();
private boolean exclude = false;
private boolean initialised = false;
private List<QName> peerAssociationTypes = new ArrayList<QName>();
private ServiceRegistry serviceRegistry;
public PeerAssociatedNodeFinder()
{
}
public PeerAssociatedNodeFinder(Collection<QName> associationTypeNames)
{
setAssociationTypes(associationTypeNames);
}
public PeerAssociatedNodeFinder(QName... associationTypeNames)
{
setAssociationTypes(associationTypeNames);
}
public PeerAssociatedNodeFinder(Collection<QName> associationTypeNames, boolean exclude)
{
setAssociationTypes(associationTypeNames);
this.exclude = exclude;
}
public PeerAssociatedNodeFinder(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
public PeerAssociatedNodeFinder(ServiceRegistry serviceRegistry, Collection<QName> associationTypeNames)
{
this(serviceRegistry);
setAssociationTypes(associationTypeNames);
}
public PeerAssociatedNodeFinder(ServiceRegistry serviceRegistry, QName... associationTypeNames)
{
this(serviceRegistry);
setAssociationTypes(associationTypeNames);
}
public PeerAssociatedNodeFinder(ServiceRegistry serviceRegistry, Collection<QName> associationTypeNames, boolean exclude)
{
this(serviceRegistry);
setAssociationTypes(associationTypeNames);
this.exclude = exclude;
}
/**
* @param exclude
* Set to true to exclude the specified association types, and false to include only the specified
* association types.
*/
public void setExclude(boolean exclude)
{
this.exclude = exclude;
}
public void setAssociationTypes(QName... associationTypes)
{
setAssociationTypes(Arrays.asList(associationTypes));
}
public void setAssociationTypes(Collection<QName> associationTypes)
{
suppliedAssociationTypes = new HashSet<QName>(associationTypes);
initialised = false;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.service.cmr.transfer.NodeFinder#findFrom(org.alfresco.service.cmr.repository.NodeRef,
* org.alfresco.service.ServiceRegistry)
*/
public Set<NodeRef> findFrom(NodeRef thisNode)
{
if (!initialised)
{
init();
}
if (exclude)
{
return processExcludedSet(thisNode);
}
else
{
return processIncludedSet(thisNode);
}
}
/**
* @param thisNode
* @param serviceRegistry
* @return
*/
private Set<NodeRef> processExcludedSet(NodeRef thisNode)
{
Set<NodeRef> results = new HashSet<NodeRef>(89);
NodeService nodeService = serviceRegistry.getNodeService();
// Find any peer nodes (filtering as necessary)
List<AssociationRef> targets = nodeService.getTargetAssocs(thisNode, RegexQNamePattern.MATCH_ALL);
boolean filterPeers = !peerAssociationTypes.isEmpty();
for (AssociationRef target : targets)
{
if (!filterPeers || !peerAssociationTypes.contains(target.getTypeQName()))
{
results.add(target.getTargetRef());
}
}
return results;
}
private Set<NodeRef> processIncludedSet(NodeRef startingNode)
{
NodeService nodeService = serviceRegistry.getNodeService();
Set<NodeRef> foundNodes = new HashSet<NodeRef>(89);
for (QName assocType : peerAssociationTypes)
{
List<AssociationRef> targets = nodeService.getTargetAssocs(startingNode, assocType);
for (AssociationRef target : targets)
{
foundNodes.add(target.getTargetRef());
}
}
return foundNodes;
}
public void init()
{
ParameterCheck.mandatory("serviceRegistry", serviceRegistry);
DictionaryService dictionaryService = serviceRegistry.getDictionaryService();
peerAssociationTypes.clear();
for (QName associationType : suppliedAssociationTypes)
{
AssociationDefinition assocDef = dictionaryService.getAssociation(associationType);
if (assocDef != null && !assocDef.isChild())
{
peerAssociationTypes.add(associationType);
}
}
initialised = true;
}
/**
* @param serviceRegistry
* the serviceRegistry to set
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
}

View File

@@ -0,0 +1,612 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.transfer.CorrespondingNodeResolver.ResolvedParentChildPair;
import org.alfresco.repo.transfer.manifest.TransferManifestDeletedNode;
import org.alfresco.repo.transfer.manifest.TransferManifestHeader;
import org.alfresco.repo.transfer.manifest.TransferManifestNode;
import org.alfresco.repo.transfer.manifest.TransferManifestNormalNode;
import org.alfresco.repo.transfer.manifest.TransferManifestProcessor;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
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.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author brian
*
*/
public class RepoPrimaryManifestProcessorImpl implements TransferManifestProcessor
{
private static final Log log = LogFactory.getLog(RepoPrimaryManifestProcessorImpl.class);
private static final String MSG_NO_PRIMARY_PARENT_SUPPLIED = "transfer_service.receiver.no_primary_parent_supplied";
private static final String MSG_ORPHANS_EXIST = "transfer_service.receiver.orphans_exist";
private static final String MSG_REFERENCED_CONTENT_FILE_MISSING = "transfer_service.receiver.content_file_missing";
protected static final Set<QName> DEFAULT_LOCAL_PROPERTIES = new HashSet<QName>();
static
{
DEFAULT_LOCAL_PROPERTIES.add(ContentModel.PROP_STORE_IDENTIFIER);
DEFAULT_LOCAL_PROPERTIES.add(ContentModel.PROP_STORE_NAME);
DEFAULT_LOCAL_PROPERTIES.add(ContentModel.PROP_STORE_PROTOCOL);
DEFAULT_LOCAL_PROPERTIES.add(ContentModel.PROP_NODE_DBID);
DEFAULT_LOCAL_PROPERTIES.add(ContentModel.PROP_NODE_REF);
DEFAULT_LOCAL_PROPERTIES.add(ContentModel.PROP_NODE_UUID);
}
private TransferReceiver receiver;
private String transferId;
private NodeService nodeService;
private ContentService contentService;
private CorrespondingNodeResolver nodeResolver;
private Map<NodeRef, List<ChildAssociationRef>> orphans = new HashMap<NodeRef, List<ChildAssociationRef>>(89);
/**
* @param transferId
*/
public RepoPrimaryManifestProcessorImpl(TransferReceiver receiver, String transferId)
{
this.receiver = receiver;
this.transferId = transferId;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.repo.transfer.manifest.TransferManifestProcessor#endTransferManifest()
*/
public void endTransferManifest()
{
if (!orphans.isEmpty())
{
error(MSG_ORPHANS_EXIST);
}
}
/**
*
*/
public void processTransferManifestNode(TransferManifestDeletedNode node)
{
// This is a deleted node. First we need to check whether it has already been deleted in this repo
// too by looking in the local archive store. If we find it then we need not do anything.
// If we can't find it in our archive store then we'll see if we can find a corresponding node in the
// store in which its old parent lives.
// If we can find a corresponding node then we'll delete it.
// If we can't find a corresponding node then we'll do nothing.
if (!nodeService.exists(node.getNodeRef()))
{
// It's not in our archive store. Check to see if we can find it in its original store...
ChildAssociationRef origPrimaryParent = node.getPrimaryParentAssoc();
NodeRef origNodeRef = new NodeRef(origPrimaryParent.getParentRef().getStoreRef(), node.getNodeRef().getId());
CorrespondingNodeResolver.ResolvedParentChildPair resolvedNodes = nodeResolver.resolveCorrespondingNode(
origNodeRef, origPrimaryParent, node.getParentPath());
// Does a corresponding node exist in this repo?
if (resolvedNodes.resolvedChild != null)
{
// Yes, it does. Delete it.
if (log.isDebugEnabled())
{
log.debug("Incoming deleted noderef " + node.getNodeRef() +
" has been resolved to existing local noderef " + resolvedNodes.resolvedChild +
" - deleting");
}
nodeService.deleteNode(resolvedNodes.resolvedChild);
}
else
{
if (log.isDebugEnabled())
{
log.debug("Incoming deleted noderef has no corresponding local noderef: " + node.getNodeRef() +
" - ignoring");
}
}
}
}
/*
* (non-Javadoc)
*
* @see
* org.alfresco.repo.transfer.manifest.TransferManifestProcessor#processTransferManifestNode(org.alfresco.repo.transfer
* .manifest.TransferManifestNode)
*/
public void processTransferManifestNode(TransferManifestNormalNode node)
{
try
{
if (log.isDebugEnabled())
{
log.debug("Processing node with incoming noderef of " + node.getNodeRef());
}
ChildAssociationRef primaryParentAssoc = getPrimaryParent(node);
if (primaryParentAssoc == null)
{
error(node, MSG_NO_PRIMARY_PARENT_SUPPLIED);
}
CorrespondingNodeResolver.ResolvedParentChildPair resolvedNodes = nodeResolver.resolveCorrespondingNode(
node.getNodeRef(), primaryParentAssoc, node.getParentPath());
// Does a corresponding node exist in this repo?
if (resolvedNodes.resolvedChild != null)
{
// Yes, it does. Update it.
if (log.isDebugEnabled())
{
log.debug("Incoming noderef " + node.getNodeRef() +
" has been resolved to existing local noderef " + resolvedNodes.resolvedChild);
}
update(node, resolvedNodes, primaryParentAssoc);
}
else
{
// No, there is no corresponding node. Worth just quickly checking the archive store...
NodeRef archiveNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, node.getNodeRef().getId());
if (nodeService.exists(archiveNodeRef))
{
// We have found a node in the archive store that has the same UUID as the one that we've
// been sent. We'll restore that archived node to a temporary location and then try
// processing this node again
if (log.isInfoEnabled())
{
log.info("Located an archived node with UUID matching transferred node: " + archiveNodeRef);
log.info("Attempting to restore " + archiveNodeRef);
}
ChildAssociationRef tempLocation = getTemporaryLocation(node.getNodeRef());
NodeRef restoredNodeRef = nodeService.restoreNode(archiveNodeRef, tempLocation.getParentRef(),
tempLocation.getTypeQName(), tempLocation.getQName());
if (log.isInfoEnabled())
{
log.info("Successfully restored node as " + restoredNodeRef + " - retrying transferred node");
}
processTransferManifestNode(node);
return;
}
if (log.isDebugEnabled())
{
log.debug("Incoming noderef has no corresponding local noderef: " + node.getNodeRef());
}
create(node, resolvedNodes, primaryParentAssoc);
}
}
catch (TransferProcessingException ex)
{
log.error("transfer processing exception" + ex.toString(), ex);
//TODO MER BUGBUG - What to do here? probably can't just swallow it
// does this mean that the manifest is stuffed?
}
}
/**
* Given the node ref, this method constructs the appropriate ChildAssociationRef that would place this node in the
* transfer's temporary folder. Useful when handling orphans.
*
* @param nodeRef
* @return
*/
private ChildAssociationRef getTemporaryLocation(NodeRef nodeRef)
{
NodeRef parentNodeRef = receiver.getTempFolder(transferId);
QName parentAssocType = ContentModel.ASSOC_CONTAINS;
QName parentAssocName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, nodeRef.getId());
return new ChildAssociationRef(parentAssocType, parentNodeRef, parentAssocName, nodeRef, true, -1);
}
/**
*
* @param node
* @param resolvedNodes
* @param primaryParentAssoc
*/
private void create(TransferManifestNormalNode node, ResolvedParentChildPair resolvedNodes,
ChildAssociationRef primaryParentAssoc)
{
log.info("Creating new node with noderef " + node.getNodeRef());
QName parentAssocType = primaryParentAssoc.getTypeQName();
QName parentAssocName = primaryParentAssoc.getQName();
NodeRef parentNodeRef = resolvedNodes.resolvedParent;
if (parentNodeRef == null)
{
if (log.isDebugEnabled())
{
log.debug("Unable to resolve parent for inbound noderef " + node.getNodeRef() +
".\n Supplied parent noderef is " + primaryParentAssoc.getParentRef() +
".\n Supplied parent path is " + node.getParentPath().toString());
}
// We can't find the node's parent.
// We'll store the node in a temporary location and record it for later processing
ChildAssociationRef tempLocation = getTemporaryLocation(node.getNodeRef());
parentNodeRef = tempLocation.getParentRef();
parentAssocType = tempLocation.getTypeQName();
parentAssocName = tempLocation.getQName();
log.info("Recording orphaned transfer node: " + node.getNodeRef());
storeOrphanNode(primaryParentAssoc);
}
// We now know that this is a new node, and we have found the appropriate parent node in the
// local repository.
log.info("Resolved parent node to " + parentNodeRef);
// We need to process content properties separately.
// First, create a shallow copy of the supplied property map...
Map<QName, Serializable> props = new HashMap<QName, Serializable>(node.getProperties());
// Split out the content properties and sanitise the others
Map<QName, Serializable> contentProps = processProperties(null, props, true);
// Create the corresponding node...
ChildAssociationRef newNode = nodeService.createNode(parentNodeRef, parentAssocType, parentAssocName,
node.getType(), props);
if (log.isDebugEnabled())
{
log.debug("Created new node (" + newNode.getChildRef() + ") parented by node " + newNode.getParentRef());
}
// Deal with the content properties
writeContent(newNode.getChildRef(), contentProps);
// Apply any aspects that are needed but haven't automatically been applied
Set<QName> aspects = new HashSet<QName>(node.getAspects());
aspects.removeAll(nodeService.getAspects(newNode.getChildRef()));
for (QName aspect : aspects)
{
nodeService.addAspect(newNode.getChildRef(), aspect, null);
}
// Is the node that we've just added the parent of any orphans that we've found earlier?
List<ChildAssociationRef> orphansToClaim = orphans.get(newNode.getChildRef());
if (orphansToClaim != null)
{
// Yes, it is...
for (ChildAssociationRef orphan : orphansToClaim)
{
nodeService.moveNode(orphan.getChildRef(), orphan.getParentRef(), orphan.getTypeQName(),
orphan.getQName());
}
// We can now remove the record of these orphans, as their parent has been found
orphans.remove(newNode.getChildRef());
}
}
/**
*
* @param node
* @param resolvedNodes
* @param primaryParentAssoc
*/
private void update(TransferManifestNormalNode node, ResolvedParentChildPair resolvedNodes,
ChildAssociationRef primaryParentAssoc)
{
NodeRef nodeToUpdate = resolvedNodes.resolvedChild;
QName parentAssocType = primaryParentAssoc.getTypeQName();
QName parentAssocName = primaryParentAssoc.getQName();
NodeRef parentNodeRef = resolvedNodes.resolvedParent;
if (parentNodeRef == null)
{
// We can't find the node's parent.
// We'll store the node in a temporary location and record it for later processing
ChildAssociationRef tempLocation = getTemporaryLocation(node.getNodeRef());
parentNodeRef = tempLocation.getParentRef();
parentAssocType = tempLocation.getTypeQName();
parentAssocName = tempLocation.getQName();
storeOrphanNode(primaryParentAssoc);
}
// First of all, do we need to move the node? If any aspect of the primary parent association has changed
// then the answer is "yes"
ChildAssociationRef currentParent = nodeService.getPrimaryParent(nodeToUpdate);
if (!currentParent.getParentRef().equals(parentNodeRef) ||
!currentParent.getTypeQName().equals(parentAssocType) ||
!currentParent.getQName().equals(parentAssocName))
{
// Yes, we need to move the node
nodeService.moveNode(nodeToUpdate, parentNodeRef, parentAssocType, parentAssocName);
}
log.info("Resolved parent node to " + parentNodeRef);
if (updateNeeded(node, nodeToUpdate))
{
// We need to process content properties separately.
// First, create a shallow copy of the supplied property map...
Map<QName, Serializable> props = new HashMap<QName, Serializable>(node.getProperties());
// Split out the content properties and sanitise the others
Map<QName, Serializable> contentProps = processProperties(nodeToUpdate, props, false);
// Update the non-content properties
nodeService.setProperties(nodeToUpdate, props);
// Deal with the content properties
writeContent(nodeToUpdate, contentProps);
// Blend the aspects together
Set<QName> suppliedAspects = new HashSet<QName>(node.getAspects());
Set<QName> existingAspects = nodeService.getAspects(nodeToUpdate);
Set<QName> aspectsToRemove = new HashSet<QName>(existingAspects);
aspectsToRemove.removeAll(suppliedAspects);
suppliedAspects.removeAll(existingAspects);
// Now aspectsToRemove contains the set of aspects to remove
// and suppliedAspects contains the set of aspects to add
for (QName aspect : suppliedAspects)
{
nodeService.addAspect(nodeToUpdate, aspect, null);
}
for (QName aspect : aspectsToRemove)
{
nodeService.removeAspect(nodeToUpdate, aspect);
}
}
}
/**
* This method takes all the received properties and separates them into two parts. The content properties are
* removed from the non-content properties such that the non-content properties remain in the "props" map and the
* content properties are returned from this method Subsequently, any properties that are to be retained from the
* local repository are copied over into the "props" map. The result of all this is that, upon return, "props"
* contains all the non-content properties that are to be written to the local repo, and "contentProps" contains all
* the content properties that are to be written to the local repo.
*
* @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
*/
private Map<QName, Serializable> processProperties(NodeRef nodeToUpdate, Map<QName, Serializable> props,
boolean isNew)
{
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>();
// ...and copy any supplied content properties into this new map...
for (Map.Entry<QName, Serializable> propEntry : props.entrySet())
{
if (ContentData.class.isAssignableFrom(propEntry.getValue().getClass()))
{
contentProps.put(propEntry.getKey(), propEntry.getValue());
}
}
// Now we can remove the content properties from amongst the other kinds of properties
// (no removeAll on a Map...)
for (QName contentPropertyName : contentProps.keySet())
{
props.remove(contentPropertyName);
}
if (!isNew)
{
// Finally, overlay the repo-specific properties from the existing node (if there is one)
Map<QName, Serializable> existingProps = (nodeToUpdate == null) ? new HashMap<QName, Serializable>()
: nodeService.getProperties(nodeToUpdate);
for (QName localProperty : getLocalProperties())
{
Serializable existingValue = existingProps.get(localProperty);
if (existingValue != null)
{
props.put(localProperty, existingValue);
}
else
{
props.remove(localProperty);
}
}
}
return contentProps;
}
/**
* @param node
* @param nodeToUpdate
* @param contentProps
*/
private void writeContent(NodeRef nodeToUpdate, Map<QName, Serializable> contentProps)
{
File stagingDir = receiver.getStagingFolder(transferId);
for (Map.Entry<QName, Serializable> contentEntry : contentProps.entrySet())
{
ContentData contentData = (ContentData) contentEntry.getValue();
String contentUrl = contentData.getContentUrl();
String fileName = contentUrl.substring(contentUrl.lastIndexOf('/') + 1);
File stagedFile = new File(stagingDir, fileName);
if (!stagedFile.exists())
{
error(MSG_REFERENCED_CONTENT_FILE_MISSING);
}
ContentWriter writer = contentService.getWriter(nodeToUpdate, contentEntry.getKey(), true);
writer.setEncoding(contentData.getEncoding());
writer.setMimetype(contentData.getMimetype());
writer.setLocale(contentData.getLocale());
writer.putContent(stagedFile);
}
}
protected boolean updateNeeded(TransferManifestNode node, NodeRef nodeToUpdate)
{
return true;
// TODO MER - Temp commenting out.
// //Assumption: if the modified and modifier properties haven't changed, and the cm:content property
// //(if it exists) hasn't changed size then we can assume that properties don't need to be updated...
// Map<QName, Serializable> suppliedProps = node.getProperties();
// Date suppliedModifiedDate = (Date)suppliedProps.get(ContentModel.PROP_MODIFIED);
// String suppliedModifier = (String)suppliedProps.get(ContentModel.PROP_MODIFIER);
// ContentData suppliedContent = (ContentData)suppliedProps.get(ContentModel.PROP_CONTENT);
//
// Map<QName, Serializable> existingProps = nodeService.getProperties(nodeToUpdate);
// Date existingModifiedDate = (Date)existingProps.get(ContentModel.PROP_MODIFIED);
// String existingModifier = (String)existingProps.get(ContentModel.PROP_MODIFIER);
// ContentData existingContent = (ContentData)existingProps.get(ContentModel.PROP_CONTENT);
//
// boolean updateNeeded = false;
// updateNeeded |= ((suppliedModifiedDate != null && !suppliedModifiedDate.equals(existingModifiedDate)) ||
// (existingModifiedDate != null && !existingModifiedDate.equals(suppliedModifiedDate)));
// updateNeeded |= ((suppliedContent != null && existingContent == null) ||
// (suppliedContent == null && existingContent != null) ||
// (suppliedContent != null && existingContent != null && suppliedContent.getSize() !=
// existingContent.getSize()));
// updateNeeded |= ((suppliedModifier != null && !suppliedModifier.equals(existingModifier)) ||
// (existingModifier != null && !existingModifier.equals(suppliedModifier)));
// return updateNeeded;
}
/**
* @return
*/
protected Set<QName> getLocalProperties()
{
return DEFAULT_LOCAL_PROPERTIES;
}
/**
* @param primaryParentAssoc
*/
private void storeOrphanNode(ChildAssociationRef primaryParentAssoc)
{
List<ChildAssociationRef> orphansOfParent = orphans.get(primaryParentAssoc.getParentRef());
if (orphansOfParent == null)
{
orphansOfParent = new ArrayList<ChildAssociationRef>();
orphans.put(primaryParentAssoc.getParentRef(), orphansOfParent);
}
orphansOfParent.add(primaryParentAssoc);
}
/**
* @param node
* @param msgId
*/
private void error(TransferManifestNode node, String msgId)
{
TransferProcessingException ex = new TransferProcessingException(msgId);
log.error(ex.getMessage(), ex);
throw ex;
}
/**
* @param msgId
*/
private void error(String msgId)
{
TransferProcessingException ex = new TransferProcessingException(msgId);
log.error(ex.getMessage(), ex);
throw ex;
}
/**
* @param node
* @return
*/
private ChildAssociationRef getPrimaryParent(TransferManifestNormalNode node)
{
List<ChildAssociationRef> parents = node.getParentAssocs();
for (ChildAssociationRef parent : parents)
{
if (parent.isPrimary())
return parent;
}
return null;
}
/*
* (non-Javadoc)
*
* @see
* org.alfresco.repo.transfer.manifest.TransferManifestProcessor#processTransferManifiestHeader(org.alfresco.repo
* .transfer.manifest.TransferManifestHeader)
*/
public void processTransferManifiestHeader(TransferManifestHeader header)
{
}
/*
* (non-Javadoc)
*
* @see org.alfresco.repo.transfer.manifest.TransferManifestProcessor#startTransferManifest()
*/
public void startTransferManifest()
{
}
/**
* @param nodeService
* the nodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param contentService
* the contentService to set
*/
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
/**
* @param nodeResolver
* the nodeResolver to set
*/
public void setNodeResolver(CorrespondingNodeResolver nodeResolver)
{
this.nodeResolver = nodeResolver;
}
}

View File

@@ -0,0 +1,292 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.manifest.TransferManifestProcessor;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.RegexQNamePattern;
/**
* @author brian
*
*/
public class RepoSecondaryManifestProcessorImpl implements TransferManifestProcessor
{
private NodeService nodeService;
private CorrespondingNodeResolver nodeResolver;
private String transferId;
/**
* @param transferId
*/
public RepoSecondaryManifestProcessorImpl(String transferId)
{
this.transferId = transferId;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.repo.transfer.manifest.TransferManifestProcessor#endTransferManifest()
*/
public void endTransferManifest()
{
// TODO Auto-generated method stub
}
/**
*
*/
public void processTransferManifestNode(TransferManifestDeletedNode node)
{
}
/*
* (non-Javadoc)
*
* @see
* org.alfresco.repo.transfer.manifest.TransferManifestProcessor#processTransferManifestNode(org.alfresco.repo.transfer
* .manifest.TransferManifestNode)
*/
public void processTransferManifestNode(TransferManifestNormalNode node)
{
NodeRef correspondingNodeRef = nodeResolver.resolveCorrespondingNode(node.getNodeRef(),
TransferManifestNodeHelper.getPrimaryParentAssoc(node), node.getParentPath()).resolvedChild;
if (correspondingNodeRef == null)
{
correspondingNodeRef = node.getNodeRef();
}
//Process parent assocs...
List<ChildAssociationRef> requiredAssocs = node.getParentAssocs();
List<ChildAssociationRef> currentAssocs = nodeService.getParentAssocs(correspondingNodeRef);
processParentChildAssociations(requiredAssocs, currentAssocs, correspondingNodeRef, false);
//Process child assocs...
requiredAssocs = node.getChildAssocs();
currentAssocs = nodeService.getChildAssocs(correspondingNodeRef);
processParentChildAssociations(requiredAssocs, currentAssocs, correspondingNodeRef, true);
//Process "target" peer associations (associations *from* this node)
List<AssociationRef> requiredPeerAssocs = node.getTargetAssocs();
List<AssociationRef> currentPeerAssocs = nodeService.getTargetAssocs(correspondingNodeRef, RegexQNamePattern.MATCH_ALL);
processPeerAssociations(requiredPeerAssocs, currentPeerAssocs, correspondingNodeRef, true);
//Process "source" peer associations (associations *to* this node)
requiredPeerAssocs = node.getSourceAssocs();
currentPeerAssocs = nodeService.getSourceAssocs(correspondingNodeRef, RegexQNamePattern.MATCH_ALL);
processPeerAssociations(requiredPeerAssocs, currentPeerAssocs, correspondingNodeRef, true);
}
/**
* @param requiredAssocs
* @param currentAssocs
* @param correspondingNodeRef
* @param isSource
*/
private void processPeerAssociations(List<AssociationRef> requiredAssocs,
List<AssociationRef> currentAssocs, NodeRef nodeRef, boolean isSource)
{
if (requiredAssocs == null) {
requiredAssocs = new ArrayList<AssociationRef>();
}
if (currentAssocs == null) {
currentAssocs = new ArrayList<AssociationRef>();
}
List<AssociationRef> assocsToAdd = new ArrayList<AssociationRef>();
List<AssociationRef> assocsToRemove = new ArrayList<AssociationRef>();
Map<NodeRef, AssociationRef> currentAssocMap = new HashMap<NodeRef, AssociationRef>();
for (AssociationRef currentAssoc : currentAssocs) {
NodeRef otherNode = isSource ? currentAssoc.getTargetRef() : currentAssoc.getSourceRef();
currentAssocMap.put(otherNode, currentAssoc);
}
for (AssociationRef requiredAssoc : requiredAssocs)
{
NodeRef otherNode = isSource ? requiredAssoc.getTargetRef() : requiredAssoc.getSourceRef();
AssociationRef existingAssociation = currentAssocMap.remove(otherNode);
if (existingAssociation != null) {
//We already have an association with the required node.
//Check whether it is correct
if (!existingAssociation.getTypeQName().equals(requiredAssoc.getTypeQName())) {
//No, the existing one doesn't match the required one
assocsToRemove.add(existingAssociation);
assocsToAdd.add(requiredAssoc);
}
} else {
//We don't have an existing association with this required node
//Check that the required node exists in this repo, and record it for adding
//if it does
if (nodeService.exists(otherNode)) {
assocsToAdd.add(requiredAssoc);
}
}
}
//Once we get here, any entries remaining in currentParentMap are associations that need to be deleted.
assocsToRemove.addAll(currentAssocMap.values());
//Deal with associations to be removed
for (AssociationRef assocToRemove : assocsToRemove) {
nodeService.removeAssociation(assocToRemove.getSourceRef(), assocToRemove.getTargetRef(), assocToRemove.getTypeQName());
}
//Deal with associations to be added
for (AssociationRef assocToAdd : assocsToAdd) {
NodeRef source = isSource ? nodeRef : assocToAdd.getSourceRef();
NodeRef target = isSource ? assocToAdd.getTargetRef() : nodeRef;
nodeService.createAssociation(source, target, assocToAdd.getTypeQName());
}
}
private void processParentChildAssociations(List<ChildAssociationRef> requiredAssocs,
List<ChildAssociationRef> currentAssocs, NodeRef nodeRef, boolean isParent) {
if (requiredAssocs == null) {
requiredAssocs = new ArrayList<ChildAssociationRef>();
}
if (currentAssocs == null) {
currentAssocs = new ArrayList<ChildAssociationRef>();
}
List<ChildAssociationRef> assocsToAdd = new ArrayList<ChildAssociationRef>();
List<ChildAssociationRef> assocsToRemove = new ArrayList<ChildAssociationRef>();
Map<NodeRef, ChildAssociationRef> currentAssocMap = new HashMap<NodeRef, ChildAssociationRef>();
for (ChildAssociationRef currentAssoc : currentAssocs) {
if (!currentAssoc.isPrimary()) {
NodeRef key = isParent ? currentAssoc.getChildRef() : currentAssoc.getParentRef();
currentAssocMap.put(key, currentAssoc);
}
}
for (ChildAssociationRef requiredAssoc : requiredAssocs)
{
// We skip the primary parent, since this has already been handled
if (!requiredAssoc.isPrimary())
{
NodeRef otherNode = isParent ? requiredAssoc.getChildRef() : requiredAssoc.getParentRef();
ChildAssociationRef existingAssociation = currentAssocMap.remove(otherNode);
if (existingAssociation != null) {
//We already have an association with the required parent.
//Check whether it is correct
if (!existingAssociation.getQName().equals(requiredAssoc.getQName()) ||
!existingAssociation.getTypeQName().equals(requiredAssoc.getTypeQName())) {
//No, the existing one doesn't match the required one
assocsToRemove.add(existingAssociation);
assocsToAdd.add(requiredAssoc);
}
} else {
//We don't have an existing association with this required parent
//Check that the requiredParent exists in this repo, and record it for adding
//if it does
if (nodeService.exists(otherNode)) {
assocsToAdd.add(requiredAssoc);
}
}
}
}
//Once we get here, any entries remaining in currentParentMap are associations that need to be deleted.
assocsToRemove.addAll(currentAssocMap.values());
//Deal with associations to be removed
for (ChildAssociationRef assocToRemove : assocsToRemove) {
nodeService.removeChildAssociation(assocToRemove);
}
//Deal with associations to be added
for (ChildAssociationRef assocToAdd : assocsToAdd) {
NodeRef parent = isParent ? nodeRef : assocToAdd.getParentRef();
NodeRef child = isParent ? assocToAdd.getChildRef() : nodeRef;
nodeService.addChild(parent, child,
assocToAdd.getTypeQName(), assocToAdd.getQName());
}
}
/*
* (non-Javadoc)
*
* @see
* org.alfresco.repo.transfer.manifest.TransferManifestProcessor#processTransferManifiestHeader(org.alfresco.repo
* .transfer.manifest.TransferManifestHeader)
*/
public void processTransferManifiestHeader(TransferManifestHeader header)
{
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see org.alfresco.repo.transfer.manifest.TransferManifestProcessor#startTransferManifest()
*/
public void startTransferManifest()
{
// TODO Auto-generated method stub
}
/**
* @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

@@ -0,0 +1,633 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transfer.manifest.TransferManifestProcessor;
import org.alfresco.repo.transfer.manifest.XMLTransferManifestReader;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
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.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.transfer.TransferException;
import org.alfresco.service.cmr.transfer.TransferReceiver;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.FileCopyUtils;
/**
* @author brian
*
*/
public class RepoTransferReceiverImpl implements TransferReceiver
{
private final static Log log = LogFactory.getLog(RepoTransferReceiverImpl.class);
private static final String MSG_FAILED_TO_CREATE_STAGING_FOLDER = "transfer_service.receiver.failed_to_create_staging_folder";
private static final String MSG_TRANSFER_LOCK_FOLDER_NOT_FOUND = "transfer_service.receiver.lock_folder_not_found";
private static final String MSG_TRANSFER_TEMP_FOLDER_NOT_FOUND = "transfer_service.receiver.temp_folder_not_found";
private static final String MSG_TRANSFER_LOCK_UNAVAILABLE = "transfer_service.receiver.lock_unavailable";
private static final String MSG_INBOUND_TRANSFER_FOLDER_NOT_FOUND = "transfer_service.receiver.record_folder_not_found";
private static final String MSG_NOT_LOCK_OWNER = "transfer_service.receiver.not_lock_owner";
private static final String MSG_ERROR_WHILE_ENDING_TRANSFER = "transfer_service.receiver.error_ending_transfer";
private static final String MSG_ERROR_WHILE_STAGING_SNAPSHOT = "transfer_service.receiver.error_staging_snapshot";
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 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";
private NodeService nodeService;
private SearchService searchService;
private TransactionService transactionService;
private String transferLockFolderPath;
private String inboundTransferRecordsPath;
private String rootStagingDirectory;
private String transferTempFolderPath;
private ManifestProcessorFactory manifestProcessorFactory;
private BehaviourFilter behaviourFilter;
private NodeRef transferLockFolder;
private NodeRef transferTempFolder;
private NodeRef inboundTransferRecordsFolder;
public void init()
{
PropertyCheck.mandatory(this, "nodeService", nodeService);
PropertyCheck.mandatory(this, "searchService", searchService);
PropertyCheck.mandatory(this, "transactionService", transactionService);
PropertyCheck.mandatory(this, "transferLockFolderPath", transferLockFolderPath);
PropertyCheck.mandatory(this, "inboundTransferRecordsPath", inboundTransferRecordsPath);
PropertyCheck.mandatory(this, "rootStagingDirectory", rootStagingDirectory);
}
/*
* (non-Javadoc)
*
* @see
* org.alfresco.repo.web.scripts.transfer.TransferReceiver#getStagingFolder(org.alfresco.service.cmr.repository.
* NodeRef)
*/
public File getStagingFolder(String transferId)
{
if (transferId == null)
{
throw new IllegalArgumentException("transferId = " + transferId);
}
NodeRef transferNodeRef = new NodeRef(transferId);
File tempFolder;
String tempFolderPath = rootStagingDirectory + "/" + transferNodeRef.getId();
tempFolder = new File(tempFolderPath);
if (!tempFolder.exists())
{
if (!tempFolder.mkdirs())
{
tempFolder = null;
throw new TransferException(MSG_FAILED_TO_CREATE_STAGING_FOLDER, new Object[] {transferId});
}
}
return tempFolder;
}
private NodeRef getLockFolder()
{
// Have we already resolved the node that is the parent of the lock node?
// If not then do so.
if (transferLockFolder == null)
{
synchronized (this)
{
if (transferLockFolder == null)
{
ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
SearchService.LANGUAGE_LUCENE, "PATH:\"" + transferLockFolderPath + "\"");
if (rs.length() > 0)
{
transferLockFolder = rs.getNodeRef(0);
} else
{
throw new TransferException(MSG_TRANSFER_LOCK_FOLDER_NOT_FOUND, new Object[] {transferLockFolderPath});
}
}
}
}
return transferLockFolder;
}
public NodeRef getTempFolder(String transferId)
{
// Have we already resolved the node that is the temp folder?
// If not then do so.
if (transferTempFolder == null)
{
synchronized (this)
{
if (transferTempFolder == null)
{
ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
SearchService.LANGUAGE_LUCENE, "PATH:\"" + transferTempFolderPath + "\"");
if (rs.length() > 0)
{
transferTempFolder = rs.getNodeRef(0);
} else
{
throw new TransferException(MSG_TRANSFER_TEMP_FOLDER_NOT_FOUND, new Object[] {transferId, transferTempFolderPath});
}
}
}
}
NodeRef transferNodeRef = new NodeRef(transferId);
String tempTransferFolderName = transferNodeRef.getId();
NodeRef tempFolderNode = null;
QName folderName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, tempTransferFolderName);
// Do we already have a temp folder for this transfer?
List<ChildAssociationRef> tempChildren = nodeService.getChildAssocs(transferTempFolder,
RegexQNamePattern.MATCH_ALL, folderName);
if (tempChildren.isEmpty())
{
// No, we don't have a temp folder for this transfer yet. Create it...
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NAME, tempTransferFolderName);
tempFolderNode = nodeService.createNode(transferTempFolder, ContentModel.ASSOC_CONTAINS, folderName,
ContentModel.TYPE_FOLDER, props).getChildRef();
} else
{
// Yes, we do have a temp folder for this transfer already. Return it.
tempFolderNode = tempChildren.get(0).getChildRef();
}
return tempFolderNode;
}
/*
* (non-Javadoc)
*
* @see org.alfresco.repo.web.scripts.transfer.TransferReceiver#start()
*/
public String start()
{
log.debug("start");
final NodeRef relatedTransferRecord = createTransferRecord();
final NodeRef lockFolder = getLockFolder();
RetryingTransactionHelper txHelper = transactionService.getRetryingTransactionHelper();
try
{
txHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NAME, LOCK_FILE_NAME);
props.put(TransferModel.PROP_TRANSFER_ID, relatedTransferRecord.toString());
log.error("Creating transfer lock associated with this transfer record: " + relatedTransferRecord);
ChildAssociationRef assoc = nodeService.createNode(lockFolder, ContentModel.ASSOC_CONTAINS,
LOCK_QNAME, TransferModel.TYPE_TRANSFER_LOCK, props);
log.error("Transfer lock created as node " + assoc.getChildRef());
return assoc.getChildRef();
}
}, false, true);
}
catch (DuplicateChildNodeNameException ex)
{
log.debug("lock is already taken");
// lock is already taken.
throw new TransferException(MSG_TRANSFER_LOCK_UNAVAILABLE);
}
String transferId = relatedTransferRecord.toString();
getStagingFolder(transferId);
return transferId;
}
/**
* @return
*/
private NodeRef createTransferRecord()
{
log.debug("->createTransferRecord");
if (inboundTransferRecordsFolder == null)
{
synchronized (this)
{
if (inboundTransferRecordsFolder == null)
{
log.debug("Trying to find transfer records folder: " + inboundTransferRecordsPath);
ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
SearchService.LANGUAGE_LUCENE, "PATH:\"" + inboundTransferRecordsPath + "\"");
if (rs.length() > 0)
{
inboundTransferRecordsFolder = rs.getNodeRef(0);
log.debug("Found inbound transfer records folder: " + inboundTransferRecordsFolder);
} else
{
throw new TransferException(MSG_INBOUND_TRANSFER_FOLDER_NOT_FOUND, new Object[] {inboundTransferRecordsPath});
}
}
}
}
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssSSSZ");
String timeNow = format.format(new Date());
QName recordName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, timeNow);
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NAME, timeNow);
log.debug("Creating transfer record with name: " + timeNow);
ChildAssociationRef assoc = nodeService.createNode(inboundTransferRecordsFolder, ContentModel.ASSOC_CONTAINS,
recordName, ContentModel.TYPE_CONTENT, props);
log.debug("<-createTransferRecord: " + assoc.getChildRef());
return assoc.getChildRef();
}
/*
* (non-Javadoc)
*
* @see org.alfresco.repo.web.scripts.transfer.TransferReceiver#end(org.alfresco.service.cmr.repository.NodeRef)
*/
public void end(final String transferId)
{
log.debug("end transferId:" + transferId);
if (transferId == null)
{
throw new IllegalArgumentException("transferId = " + transferId);
}
try
{
// We remove the lock node in a separate transaction, since it was created in a separate transaction
transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
// Find the lock node
NodeRef lockId = getLockNode();
if (lockId != null)
{
if (!testLockedTransfer(lockId, transferId))
{
throw new TransferException(MSG_NOT_LOCK_OWNER, new Object[] {transferId});
}
// Delete the lock node.
log.debug("delete lock node :" + lockId);
nodeService.deleteNode(lockId);
}
return null;
}
}, false, true);
log.debug("delete staging folder " + transferId);
// Delete the staging folder.
File stagingFolder = getStagingFolder(transferId);
deleteFile(stagingFolder);
log.debug("Staging folder deleted");
}
catch (TransferException ex)
{
throw ex;
}
catch (Exception ex)
{
throw new TransferException(MSG_ERROR_WHILE_ENDING_TRANSFER, ex);
}
}
public void abort(String transferId) throws TransferException
{
//TODO Think about the relationship between abort and end.
end(transferId);
}
public void prepare(String transferId) throws TransferException
{
}
/**
* @param stagingFolder
*/
private void deleteFile(File file)
{
if (!file.isDirectory()) file.delete();
File[] fileList = file.listFiles();
if (fileList != null) {
for (File currentFile : fileList) {
deleteFile(currentFile);
}
}
file.delete();
}
private NodeRef getLockNode()
{
final NodeRef lockFolder = getLockFolder();
List<ChildAssociationRef> assocs = nodeService.getChildAssocs(lockFolder, ContentModel.ASSOC_CONTAINS,
LOCK_QNAME);
NodeRef lockId = assocs.size() == 0 ? null : assocs.get(0).getChildRef();
return lockId;
}
private boolean testLockedTransfer(NodeRef lockId, String transferId)
{
if (lockId == null)
{
throw new IllegalArgumentException("lockId = null");
}
if (transferId == null)
{
throw new IllegalArgumentException("transferId = null");
}
String currentTransferId = (String) nodeService.getProperty(lockId, TransferModel.PROP_TRANSFER_ID);
// Check that the lock is held for the specified transfer (error if not)
return (transferId.equals(currentTransferId));
}
/*
* (non-Javadoc)
*
* @see org.alfresco.service.cmr.transfer.TransferReceiver#nudgeLock(java.lang.String)
*/
public void nudgeLock(final String transferId) throws TransferException
{
if (transferId == null)
throw new IllegalArgumentException("transferId = null");
transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
// Find the lock node
NodeRef lockId = getLockNode();
// Check that the specified transfer is the one that owns the lock
if (!testLockedTransfer(lockId, transferId))
{
throw new TransferException(MSG_NOT_LOCK_OWNER);
}
// Just write the lock file name again (no change, but forces the modified time to be updated)
nodeService.setProperty(lockId, ContentModel.PROP_NAME, LOCK_FILE_NAME);
return null;
}
}, false, true);
}
/*
* (non-Javadoc)
*
* @see org.alfresco.service.cmr.transfer.TransferReceiver#saveSnapshot(java.io.InputStream)
*/
public void saveSnapshot(String transferId, InputStream openStream) throws TransferException
{
log.debug("save snapshot transferId=" + transferId);
// Check that this transfer owns the lock and give it a nudge to stop it expiring
nudgeLock(transferId);
File snapshotFile = new File(getStagingFolder(transferId), SNAPSHOT_FILE_NAME);
try
{
if (snapshotFile.createNewFile())
{
FileCopyUtils.copy(openStream, new FileOutputStream(snapshotFile));
}
log.debug("saved snapshot for transferId=" + transferId);
}
catch (Exception ex)
{
throw new TransferException(MSG_ERROR_WHILE_STAGING_SNAPSHOT, ex);
}
}
/*
* (non-Javadoc)
*
* @see org.alfresco.service.cmr.transfer.TransferReceiver#saveContent(java.lang.String, java.lang.String,
* java.io.InputStream)
*/
public void saveContent(String transferId, String contentFileId, InputStream contentStream)
throws TransferException
{
nudgeLock(transferId);
File stagedFile = new File(getStagingFolder(transferId), contentFileId);
try
{
if (stagedFile.createNewFile())
{
FileCopyUtils.copy(contentStream, new BufferedOutputStream(new FileOutputStream(stagedFile)));
}
}
catch (Exception ex)
{
throw new TransferException(MSG_ERROR_WHILE_STAGING_CONTENT, ex);
}
}
public void commit(String transferId) throws TransferException
{
log.debug("commit transferId=" + transferId);
try
{
nudgeLock(transferId);
List<TransferManifestProcessor> commitProcessors = manifestProcessorFactory.getCommitProcessors(this, transferId);
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser parser = saxParserFactory.newSAXParser();
File snapshotFile = getSnapshotFile(transferId);
if (snapshotFile.exists())
{
log.debug("processing manifest file:" + snapshotFile.getAbsolutePath());
//We parse the file as many times as we have processors
for (TransferManifestProcessor processor : commitProcessors)
{
XMLTransferManifestReader reader = new XMLTransferManifestReader(processor);
behaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE);
try
{
parser.parse(snapshotFile, reader);
}
finally
{
behaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE);
}
nudgeLock(transferId);
parser.reset();
}
}
else
{
log.debug("no snapshot received");
throw new TransferException(MSG_NO_SNAPSHOT_RECEIVED);
}
/**
* Successfully transfred
*/
log.debug("commit success transferId=" + transferId);
}
catch (TransferException ex)
{
log.debug("unable to commit", ex);
throw ex;
}
catch (Exception ex)
{
log.debug("unable to commit", ex);
throw new TransferException(MSG_ERROR_WHILE_COMMITTING_TRANSFER, ex);
}
finally
{
/**
* Clean up at the end of the transfer
*/
try
{
log.debug("calling end");
end(transferId);
log.debug("called end");
}
catch (Exception ex)
{
log.error("Failed to clean up transfer. Lock may still be in place: " + transferId);
}
}
}
private File getSnapshotFile(String transferId)
{
return new File(getStagingFolder(transferId), SNAPSHOT_FILE_NAME);
}
/**
* @param searchService
* the searchService to set
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @param transactionService
* the transactionService to set
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* @param transferLockFolderPath
* the transferLockFolderPath to set
*/
public void setTransferLockFolderPath(String transferLockFolderPath)
{
this.transferLockFolderPath = transferLockFolderPath;
}
/**
* @param transferTempFolderPath the transferTempFolderPath to set
*/
public void setTransferTempFolderPath(String transferTempFolderPath)
{
this.transferTempFolderPath = transferTempFolderPath;
}
/**
* @param rootStagingDirectory
* the rootTransferFolder to set
*/
public void setRootStagingDirectory(String rootStagingDirectory)
{
this.rootStagingDirectory = rootStagingDirectory;
}
/**
* @param inboundTransferRecordsPath
* the inboundTransferRecordsPath to set
*/
public void setInboundTransferRecordsPath(String inboundTransferRecordsPath)
{
this.inboundTransferRecordsPath = inboundTransferRecordsPath;
}
/**
* @param nodeService
* the nodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param manifestProcessorFactory the manifestProcessorFactory to set
*/
public void setManifestProcessorFactory(ManifestProcessorFactory manifestProcessorFactory)
{
this.manifestProcessorFactory = manifestProcessorFactory;
}
/**
* @param behaviourFilter the behaviourFilter to set
*/
public void setBehaviourFilter(BehaviourFilter behaviourFilter)
{
this.behaviourFilter = behaviourFilter;
}
}

View File

@@ -0,0 +1,475 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.TransferManifestNode;
import org.alfresco.repo.transfer.manifest.TransferManifestNormalNode;
import org.alfresco.repo.transfer.manifest.XMLTransferManifestWriter;
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.Path;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.transfer.TransferException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseAlfrescoSpringTest;
import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tools.ant.filters.StringInputStream;
/**
* Unit test for RepoTransferReceiverImpl
*
* @author Brian Remmington
*/
public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest
{
private static int fileCount = 0;
private static final Log log = LogFactory.getLog(RepoTransferReceiverImplTest.class);
private RepoTransferReceiverImpl receiver;
private String dummyContent;
private byte[] dummyContentBytes;
/**
* Called during the transaction setup
*/
@SuppressWarnings("deprecation")
protected void onSetUpInTransaction() throws Exception
{
System.out.println("java.io.tmpdir == " + System.getProperty("java.io.tmpdir"));
super.onSetUpInTransaction();
// Get the required services
this.receiver = (RepoTransferReceiverImpl) this.getApplicationContext().getBean("transferReceiver");
this.dummyContent = "This is some dummy content.";
this.dummyContentBytes = dummyContent.getBytes("UTF-8");
}
public void testStartAndEnd() throws Exception
{
String transferId = receiver.start();
System.out.println("TransferId == " + transferId);
File stagingFolder = receiver.getStagingFolder(transferId);
assertTrue(receiver.getStagingFolder(transferId).exists());
try
{
receiver.start();
fail("Successfully started twice!");
} catch (TransferException ex)
{
// Expected
}
try
{
receiver.end(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, GUID.generate()).toString());
fail("Successfully ended with transfer id that doesn't own lock.");
} catch (TransferException ex)
{
// Expected
}
receiver.end(transferId);
assertFalse(stagingFolder.exists());
receiver.end(receiver.start());
}
public void testSaveContent() throws Exception
{
String transferId = receiver.start();
try
{
String contentId = "mytestcontent";
receiver.saveContent(transferId, contentId, new ByteArrayInputStream(dummyContentBytes));
File contentFile = new File(receiver.getStagingFolder(transferId), contentId);
assertTrue(contentFile.exists());
assertEquals(dummyContentBytes.length, contentFile.length());
} finally
{
receiver.end(transferId);
}
}
public void testSaveSnapshot() throws Exception
{
String transferId = receiver.start();
File snapshotFile = null;
try
{
TransferManifestNode node = createContentNode(transferId);
List<TransferManifestNode> nodes = new ArrayList<TransferManifestNode>();
nodes.add(node);
String snapshot = createSnapshot(nodes);
receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8"));
File stagingFolder = receiver.getStagingFolder(transferId);
snapshotFile = new File(stagingFolder, "snapshot.xml");
assertTrue(snapshotFile.exists());
assertEquals(snapshot.getBytes("UTF-8").length, snapshotFile.length());
} finally
{
receiver.end(transferId);
if (snapshotFile != null) {
assertFalse(snapshotFile.exists());
}
}
}
public void testBasicCommit() throws Exception {
String transferId = receiver.start();
try
{
TransferManifestNode node = createContentNode(transferId);
List<TransferManifestNode> nodes = new ArrayList<TransferManifestNode>();
nodes.add(node);
String snapshot = createSnapshot(nodes);
receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8"));
receiver.saveContent(transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes));
receiver.commit(transferId);
assertTrue(nodeService.exists(node.getNodeRef()));
nodeService.deleteNode(node.getNodeRef());
} catch (Exception ex)
{
receiver.end(transferId);
throw ex;
}
}
public void testMoreComplexCommit() throws Exception {
String transferId = receiver.start();
try
{
List<TransferManifestNode> nodes = new ArrayList<TransferManifestNode>();
TransferManifestNormalNode node1 = createContentNode(transferId);
nodes.add(node1);
TransferManifestNormalNode node2 = createContentNode(transferId);
nodes.add(node2);
TransferManifestNode node3 = createContentNode(transferId);
nodes.add(node3);
TransferManifestNode node4 = createContentNode(transferId);
nodes.add(node4);
TransferManifestNode node5 = createContentNode(transferId);
nodes.add(node5);
TransferManifestNode node6 = createContentNode(transferId);
nodes.add(node6);
TransferManifestNode node7 = createContentNode(transferId);
nodes.add(node7);
TransferManifestNode node8 = createFolderNode(transferId);
nodes.add(node8);
TransferManifestNode node9 = createFolderNode(transferId);
nodes.add(node9);
TransferManifestNode node10 = createFolderNode(transferId);
nodes.add(node10);
TransferManifestNormalNode node11 = createFolderNode(transferId);
nodes.add(node11);
TransferManifestNode node12 = createFolderNode(transferId);
nodes.add(node12);
associatePeers(node1, node2);
moveNode(node2, node11);
String snapshot = createSnapshot(nodes);
receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8"));
for (TransferManifestNode node : nodes) {
receiver.saveContent(transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes));
}
receiver.commit(transferId);
assertTrue(nodeService.getAspects(node1.getNodeRef()).contains(ContentModel.ASPECT_ATTACHABLE));
assertFalse(nodeService.getSourceAssocs(node2.getNodeRef(), ContentModel.ASSOC_ATTACHMENTS).isEmpty());
for (TransferManifestNode node : nodes) {
assertTrue(nodeService.exists(node.getNodeRef()));
}
} catch (Exception ex)
{
receiver.end(transferId);
throw ex;
}
}
public void testNodeDeleteAndRestore() throws Exception {
String transferId = receiver.start();
try
{
List<TransferManifestNode> nodes = new ArrayList<TransferManifestNode>();
TransferManifestNormalNode node1 = createContentNode(transferId);
nodes.add(node1);
TransferManifestNormalNode node2 = createContentNode(transferId);
nodes.add(node2);
TransferManifestNode node3 = createContentNode(transferId);
nodes.add(node3);
TransferManifestNode node4 = createContentNode(transferId);
nodes.add(node4);
TransferManifestNode node5 = createContentNode(transferId);
nodes.add(node5);
TransferManifestNode node6 = createContentNode(transferId);
nodes.add(node6);
TransferManifestNode node7 = createContentNode(transferId);
nodes.add(node7);
TransferManifestNode node8 = createFolderNode(transferId);
nodes.add(node8);
TransferManifestNode node9 = createFolderNode(transferId);
nodes.add(node9);
TransferManifestNode node10 = createFolderNode(transferId);
nodes.add(node10);
TransferManifestNormalNode node11 = createFolderNode(transferId);
nodes.add(node11);
TransferManifestNode node12 = createFolderNode(transferId);
nodes.add(node12);
associatePeers(node1, node2);
moveNode(node2, node11);
String snapshot = createSnapshot(nodes);
log.debug(snapshot);
receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8"));
for (TransferManifestNode node : nodes) {
receiver.saveContent(transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes));
}
receiver.commit(transferId);
assertTrue(nodeService.getAspects(node1.getNodeRef()).contains(ContentModel.ASPECT_ATTACHABLE));
assertFalse(nodeService.getSourceAssocs(node2.getNodeRef(), ContentModel.ASSOC_ATTACHMENTS).isEmpty());
for (TransferManifestNode node : nodes) {
assertTrue(nodeService.exists(node.getNodeRef()));
}
//Now delete nodes 8, 2, and 11 (2 and 11 are parent/child)
TransferManifestDeletedNode deletedNode8 = createDeletedNode(node8);
TransferManifestDeletedNode deletedNode2 = createDeletedNode(node2);
TransferManifestDeletedNode deletedNode11 = createDeletedNode(node11);
transferId = receiver.start();
snapshot = createSnapshot(Arrays.asList(new TransferManifestNode[] {deletedNode8, deletedNode2, deletedNode11}));
receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8"));
receiver.commit(transferId);
assertTrue(nodeService.exists(deletedNode8.getNodeRef()));
assertTrue(nodeService.hasAspect(deletedNode8.getNodeRef(), ContentModel.ASPECT_ARCHIVED));
assertTrue(nodeService.exists(deletedNode2.getNodeRef()));
assertTrue(nodeService.hasAspect(deletedNode2.getNodeRef(), ContentModel.ASPECT_ARCHIVED));
assertTrue(nodeService.exists(deletedNode11.getNodeRef()));
assertTrue(nodeService.hasAspect(deletedNode11.getNodeRef(), ContentModel.ASPECT_ARCHIVED));
//try to restore node 2. Expect an "orphan" failure, since its parent (node11) is deleted
transferId = receiver.start();
snapshot = createSnapshot(Arrays.asList(new TransferManifestNode[] {node2}));
log.debug(snapshot);
receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8"));
receiver.saveContent(transferId, node2.getUuid(), new ByteArrayInputStream(dummyContentBytes));
try
{
receiver.commit(transferId);
fail("Expected an exception!");
}
catch (TransferException ex)
{
assertTrue(ex.getMsgId(), ex.getMsgId().contains("orphan"));
}
} catch (Exception ex)
{
receiver.end(transferId);
throw ex;
}
}
/**
* @param nodeToDelete
* @return
*/
private TransferManifestDeletedNode createDeletedNode(TransferManifestNode nodeToDelete)
{
TransferManifestDeletedNode deletedNode = new TransferManifestDeletedNode();
deletedNode.setNodeRef(new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, nodeToDelete.getNodeRef().getId()));
deletedNode.setParentPath(nodeToDelete.getParentPath());
deletedNode.setPrimaryParentAssoc(nodeToDelete.getPrimaryParentAssoc());
deletedNode.setUuid(nodeToDelete.getUuid());
return deletedNode;
}
/**
* @param childNode
* @param newParent
*/
private void moveNode(TransferManifestNormalNode childNode, TransferManifestNormalNode newParent)
{
List<ChildAssociationRef> currentParents = childNode.getParentAssocs();
List<ChildAssociationRef> newParents = new ArrayList<ChildAssociationRef>();
for (ChildAssociationRef parent : currentParents) {
if (!parent.isPrimary()) {
newParents.add(parent);
} else {
ChildAssociationRef newPrimaryAssoc = new ChildAssociationRef(ContentModel.ASSOC_CONTAINS,
newParent.getNodeRef(), parent.getQName(), parent.getChildRef(), true, -1);
newParents.add(newPrimaryAssoc);
childNode.setPrimaryParentAssoc(newPrimaryAssoc);
Path newParentPath = new Path();
newParentPath.append(newParent.getParentPath());
newParentPath.append(new Path.ChildAssocElement(newParent.getPrimaryParentAssoc()));
childNode.setParentPath(newParentPath);
}
}
childNode.setParentAssocs(newParents);
}
private void associatePeers(TransferManifestNormalNode source, TransferManifestNormalNode target) {
List<AssociationRef> currentReferencedPeers = source.getTargetAssocs();
if (currentReferencedPeers == null) {
currentReferencedPeers = new ArrayList<AssociationRef>();
source.setTargetAssocs(currentReferencedPeers);
}
List<AssociationRef> currentRefereePeers = target.getSourceAssocs();
if (currentRefereePeers == null) {
currentRefereePeers = new ArrayList<AssociationRef>();
target.setSourceAssocs(currentRefereePeers);
}
Set<QName> aspects = source.getAspects();
if (aspects == null ) {
aspects = new HashSet<QName>();
source.setAspects(aspects);
}
aspects.add(ContentModel.ASPECT_ATTACHABLE);
AssociationRef newAssoc = new AssociationRef(source.getNodeRef(), ContentModel.ASSOC_ATTACHMENTS, target.getNodeRef());
currentRefereePeers.add(newAssoc);
currentReferencedPeers.add(newAssoc);
}
private String createSnapshot(List<TransferManifestNode> nodes) throws Exception {
XMLTransferManifestWriter manifestWriter = new XMLTransferManifestWriter();
StringWriter output = new StringWriter();
manifestWriter.startTransferManifest(output);
TransferManifestHeader header = new TransferManifestHeader();
header.setCreatedDate(new Date());
manifestWriter.writeTransferManifestHeader(header);
for (TransferManifestNode node : nodes) {
manifestWriter.writeTransferManifestNode(node);
}
manifestWriter.endTransferManifest();
return output.toString();
}
/**
* @return
*/
private TransferManifestNormalNode createContentNode(String transferId) throws Exception
{
TransferManifestNormalNode node = new TransferManifestNormalNode();
String uuid = GUID.generate();
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, uuid);
node.setNodeRef(nodeRef);
node.setUuid(uuid);
byte[] dummyContent = "This is some dummy content.".getBytes("UTF-8");
node.setType(ContentModel.TYPE_CONTENT);
NodeRef parentFolder = receiver.getTempFolder(transferId);
String nodeName = transferId + ".testnode" + getNameSuffix();
List<ChildAssociationRef> parents = new ArrayList<ChildAssociationRef>();
ChildAssociationRef primaryAssoc = new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, parentFolder, QName.createQName(
NamespaceService.CONTENT_MODEL_1_0_URI, nodeName), node.getNodeRef(), true, -1);
parents.add(primaryAssoc);
node.setParentAssocs(parents);
node.setParentPath(nodeService.getPath(parentFolder));
node.setPrimaryParentAssoc(primaryAssoc);
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NODE_UUID, uuid);
props.put(ContentModel.PROP_NAME, nodeName);
ContentData contentData = new ContentData("/" + uuid, "text/plain", dummyContent.length, "UTF-8");
props.put(ContentModel.PROP_CONTENT, contentData);
node.setProperties(props);
return node;
}
private TransferManifestNormalNode createFolderNode(String transferId) throws Exception
{
TransferManifestNormalNode node = new TransferManifestNormalNode();
String uuid = GUID.generate();
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, uuid);
node.setNodeRef(nodeRef);
node.setUuid(uuid);
node.setType(ContentModel.TYPE_FOLDER);
NodeRef parentFolder = receiver.getTempFolder(transferId);
String nodeName = transferId + ".folder" + getNameSuffix();
List<ChildAssociationRef> parents = new ArrayList<ChildAssociationRef>();
ChildAssociationRef primaryAssoc = new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, parentFolder, QName.createQName(
NamespaceService.CONTENT_MODEL_1_0_URI, nodeName), node.getNodeRef(), true, -1);
parents.add(primaryAssoc);
node.setParentAssocs(parents);
node.setParentPath(nodeService.getPath(parentFolder));
node.setPrimaryParentAssoc(primaryAssoc);
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NODE_UUID, uuid);
props.put(ContentModel.PROP_NAME, nodeName);
node.setProperties(props);
return node;
}
private String getNameSuffix() {
return "" + fileCount++;
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.transfer.NodeFilter;
import org.alfresco.service.cmr.transfer.NodeFinder;
import org.alfresco.service.cmr.transfer.TransferService;
/**
* This class can be used to build a set of node references from a given starting point. The caller can provide a list
* of {@link NodeFinder} objects and a list of {@link NodeFilter} objects. Starting with the nodes supplied by the
* caller, the crawler uses the NodeFinder objects to find other nodes. Each node that is found is then passed to the
* NodeFilter objects to determine whether it should be included or ignored. Any included nodes are then fed back into
* the NodeFinder objects to continue the crawl. This class was originally written to assist users of the
* {@link TransferService} in combination with the {@link ChildAssociatedNodeFinder} and the {@link ContentClassFilter}.
*
* @author brian
*
*/
public class StandardNodeCrawlerImpl
{
private ServiceRegistry serviceRegistry;
private List<NodeFinder> nodeFinders = new ArrayList<NodeFinder>();
private List<NodeFilter> nodeFilters = new ArrayList<NodeFilter>();
/**
*
*/
public StandardNodeCrawlerImpl()
{
super();
}
/**
* @param serviceRegistry
*/
public StandardNodeCrawlerImpl(ServiceRegistry serviceRegistry)
{
super();
this.serviceRegistry = serviceRegistry;
}
/**
* @param nodeService
* the nodeService to set
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
public Set<NodeRef> crawl(NodeRef... nodes)
{
return crawl(new HashSet<NodeRef>(Arrays.asList(nodes)));
}
public synchronized Set<NodeRef> crawl(Set<NodeRef> startingNodes)
{
init();
Queue<NodeRef> nodesToProcess = new LinkedList<NodeRef>();
nodesToProcess.addAll(startingNodes);
Set<NodeRef> resultingNodeSet = new HashSet<NodeRef>(89);
Set<NodeRef> processedNodes = new HashSet<NodeRef>(89);
// Do we have any more nodes to process?
while (nodesToProcess.peek() != null)
{
// Yes, we do. Read the next noderef from the queue.
NodeRef thisNode = nodesToProcess.poll();
// Check that we haven't already processed it. Skip it if we have, process it if we haven't
if (!processedNodes.contains(thisNode))
{
// Record the fact that we're processing this node
processedNodes.add(thisNode);
// We check this node against any filters that are in place (the nodes
// that we were given to start with are always processed)
if (startingNodes.contains(thisNode) || includeNode(thisNode))
{
resultingNodeSet.add(thisNode);
Set<NodeRef> subsequentNodes = findSubsequentNodes(thisNode);
for (NodeRef node : subsequentNodes)
{
nodesToProcess.add(node);
}
}
}
}
return resultingNodeSet;
}
/**
*
*/
private void init()
{
for (NodeFinder nodeFinder : this.nodeFinders)
{
nodeFinder.setServiceRegistry(serviceRegistry);
nodeFinder.init();
}
for (NodeFilter nodeFilter : this.nodeFilters)
{
nodeFilter.setServiceRegistry(serviceRegistry);
nodeFilter.init();
}
}
/**
* @param thisNode
* @return
*/
private Set<NodeRef> findSubsequentNodes(NodeRef thisNode)
{
Set<NodeRef> foundNodes = new HashSet<NodeRef>(89);
for (NodeFinder finder : nodeFinders)
{
foundNodes.addAll(finder.findFrom(thisNode));
}
return foundNodes;
}
/**
* @param thisNode
* @return
*/
private boolean includeNode(NodeRef thisNode)
{
boolean include = true;
for (int i = 0; include && (i < nodeFilters.size()); ++i)
{
include &= nodeFilters.get(i).accept(thisNode);
}
return include;
}
public synchronized void setNodeFinders(NodeFinder... finders)
{
nodeFinders = Arrays.asList(finders);
}
public synchronized void setNodeFilters(NodeFilter... filters)
{
nodeFilters = Arrays.asList(filters);
}
}

View File

@@ -0,0 +1,36 @@
package org.alfresco.repo.transfer;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.alfresco.service.cmr.transfer.TransferCallback;
import org.alfresco.service.cmr.transfer.TransferEvent;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class TestTransferCallback implements TransferCallback
{
/**
*
*/
private static final long serialVersionUID = 1L;
private static Log logger = LogFactory.getLog(TestTransferCallback.class);
public void processEvent(TransferEvent event)
{
logger.debug(event.toString());
events.add(event);
}
Queue<TransferEvent> events = new ConcurrentLinkedQueue<TransferEvent>();
/**
* Get the thread safe queue of events
* @return
*/
public Queue<TransferEvent> getEvents()
{
return events;
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import org.alfresco.service.cmr.transfer.TransferTarget;
/**
* Information about a transfer which is in progress.
*
* @author Mark Rogers
*/
public class Transfer
{
private String transferId;
private TransferTarget transferTarget;
public void setTransferId(String transferId)
{
this.transferId = transferId;
}
public String getTransferId()
{
return transferId;
}
// may also have capabilities of the remote system here (for when we are
// transfering accross versions)
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
else if (this == obj)
{
return true;
}
else if (obj instanceof Transfer == false)
{
return false;
}
Transfer that = (Transfer) obj;
return (this.transferId.equals(that.getTransferId()));
}
public int hashCode()
{
return transferId.hashCode();
}
/**
* @param target
*/
public void setTransferTarget(TransferTarget target)
{
this.transferTarget = target;
}
/**
* @return the transferTarget
*/
public TransferTarget getTransferTarget()
{
return transferTarget;
}
public String toString()
{
return "TransferId" + transferId + ", target:" + transferTarget ;
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.transfer.TransferDefinition;
import org.alfresco.service.cmr.transfer.TransferService;
/**
* @author brian
*
*/
public class TransferActionExecuter extends ActionExecuterAbstractBase
{
public static final String NAME = "transfer-node";
public static final String PARAM_TRANSFER_TARGET = "target-name";
private TransferService transferService;
/**
* @param transferService the transferService to set
*/
public void setTransferService(TransferService transferService)
{
this.transferService = transferService;
}
/* (non-Javadoc)
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
protected void executeImpl(Action action, NodeRef actionedUponNodeRef)
{
TransferDefinition td = new TransferDefinition();
Set<NodeRef> nodes = new HashSet<NodeRef>();
nodes.add(actionedUponNodeRef);
td.setNodes(nodes);
transferService.transfer("transferMe", td);
}
/* (non-Javadoc)
* @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List)
*/
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
//paramList.add(new ParameterDefinitionImpl(PARAM_TRANSFER_TARGET, DataTypeDefinition.TEXT, true, "Transfer Target Name"));
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing
*/
package org.alfresco.repo.transfer;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.transfer.TransferCallback;
import org.alfresco.service.cmr.transfer.TransferDefinition;
import org.alfresco.service.cmr.transfer.TransferService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Deploys a website to a remote server.
*
* TODO refactor and add to WCM services (when we support WCM deployment config)
*
* @author gavinc
*/
public class TransferAsyncAction extends ActionExecuterAbstractBase
{
public static final String ASYNC_QUEUE_NAME = "deployment";
private TransferService transferService;
private static Log logger = LogFactory.getLog(TransferAsyncAction.class);
public void init()
{
super.name = "transfer-async";
}
@Override
protected void executeImpl(Action action, NodeRef actionedUponNodeRef)
{
System.out.println("In TransferAsyncAction");
String targetName = (String)action.getParameterValue("targetName");
TransferDefinition definition = (TransferDefinition)action.getParameterValue("definition");
Set<TransferCallback> callback = (Set<TransferCallback>) action.getParameterValue("callbacks");
transferService.transfer(targetName, definition, callback);
}
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
}
public void setTransferService(TransferService transferService)
{
this.transferService = transferService;
}
public TransferService getTransferService()
{
return transferService;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
/**
* A bucket for little odds and ends for the transfer service.
*
* If this becomes a big class then refactor it away.
*
* @author Mark Rogers
*/
public class TransferCommons
{
/**
* The Mime Part Name of the manifest file
*/
public final static String PART_NAME_MANIFEST = "manifest";
/**
* Mapping between contentUrl and part name.
*
* @param URL
* @return the part name
*/
public final static String URLToPartName(String contentUrl)
{
return contentUrl.substring(contentUrl.lastIndexOf('/')+1);
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.Date;
import org.alfresco.service.cmr.transfer.TransferEvent;
/**
* An abstract implementation of TransferEvent.
* Also implements RangedTransferEvent.
* @see TransferEvent
* @see RangedTransferEvent
*/
public abstract class TransferEventImpl implements TransferEvent
{
private String message;
private TransferState state;
private boolean last = false;
private long range = 0;
private long position = 0;
private Date time = new Date();
public String getMessage()
{
return message;
}
public Date getTime()
{
return time;
}
public void setMessage(String message)
{
this.message = message;
}
public void setRange(long range)
{
this.range = range;
}
public void setPosition(long position)
{
this.position = position;
}
public void setTransferState(TransferState state)
{
this.state = state;
}
public void setTime(Date time)
{
this.time = time;
}
public TransferState getTransferState()
{
return state;
}
public void setLast(boolean last)
{
this.last = last;
}
public boolean isLast()
{
return last;
}
/**
* The position in the range
* @return
*/
public long getPosition()
{
return position;
}
/**
* The maximum range
* @return
*/
public long getRange()
{
return range;
}
public String toString()
{
return "TransferEventImpl : " + this.getTime() + ", " + this.getTransferState();
}
public boolean equals(Object obj)
{
if(obj instanceof TransferEventImpl)
{
TransferEventImpl other = (TransferEventImpl)obj;
if(other.getTransferState().equals(this.getTransferState()) &&
other.getPosition() == this.getPosition() &&
other.getTime().equals(this.getTime()))
{
return true;
}
}
return false;
}
public int hashCode()
{
// discard any high bits
return (int)this.getTime().getTime();
}
}

View File

@@ -0,0 +1,221 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.transfer.TransferCallback;
import org.alfresco.service.cmr.transfer.TransferEvent;
import org.alfresco.service.cmr.transfer.TransferEventCommittingStatus;
import org.alfresco.service.cmr.transfer.TransferEventEndState;
import org.alfresco.service.cmr.transfer.TransferEventEnterState;
import org.alfresco.service.cmr.transfer.TransferEventError;
import org.alfresco.service.cmr.transfer.TransferEventSendingContent;
import org.alfresco.service.cmr.transfer.TransferEventSendingManifest;
import org.alfresco.service.cmr.transfer.TransferEventSuccess;
/**
* Class to bring together all the transfer event stuff.
*
* One processor instance for each transfer.
*
* Observer
*
* @author Mark Rogers
*/
public class TransferEventProcessor
{
public Set<TransferCallback> observers = new HashSet<TransferCallback>();
LinkedBlockingQueue<TransferEvent> queue = new LinkedBlockingQueue<TransferEvent>();
public void addObserver(TransferCallback observer)
{
observers.add(observer);
}
public void deleteObserver(TransferCallback observer)
{
observers.remove(observer);
}
/**
*
*/
public TransferEventProcessor()
{
}
public void start()
{
setState(TransferEvent.TransferState.START);
notifyObservers();
}
public void success()
{
setState(TransferEvent.TransferState.SUCCESS);
/**
* Write the success event
*/
TransferEventSuccess event = new TransferEventSuccess();
event.setTransferState(TransferEvent.TransferState.SUCCESS);
event.setLast(true);
queue.add(event);
notifyObservers();
}
public void error(Exception exception)
{
setState(TransferEvent.TransferState.ERROR);
/**
* Write the error event
*/
TransferEventError event = new TransferEventError();
event.setTransferState(TransferEvent.TransferState.ERROR);
event.setLast(true);
event.setException(exception);
queue.add(event);
notifyObservers();
}
/**
*
* @param data
* @param range
* @param position
*/
public void sendContent(ContentData data, long range, long position)
{
setState(TransferEvent.TransferState.SENDING_CONTENT);
TransferEventSendingContent event = new TransferEventSendingContent();
event.setTransferState(TransferEvent.TransferState.SENDING_CONTENT);
event.setRange(range);
event.setPosition(position);
event.setSize(data.getSize());
event.setMessage("sending content " + position + " of " + range + ", size: " + event.getSize());
queue.add(event);
notifyObservers();
}
/**
*
* @param data
* @param range
* @param position
*/
public void sendManifest(long range, long position)
{
setState(TransferEvent.TransferState.SENDING_MANIFEST);
TransferEventSendingManifest event = new TransferEventSendingManifest();
event.setTransferState(TransferEvent.TransferState.SENDING_MANIFEST);
event.setRange(range);
event.setPosition(position);
event.setMessage("sending manifest");
queue.add(event);
notifyObservers();
}
public void prepare()
{
setState(TransferEvent.TransferState.PREPARING);
notifyObservers();
}
/**
*
* @param range
* @param position
*/
public void committing(long range, long position)
{
setState(TransferEvent.TransferState.COMMITTING);
TransferEventCommittingStatus event = new TransferEventCommittingStatus();
event.setTransferState(TransferEvent.TransferState.COMMITTING);
event.setRange(range);
event.setPosition(position);
event.setMessage("committing " + position + " of " + range);
queue.add(event);
notifyObservers();
}
public void abort()
{
}
private TransferEvent.TransferState currentState;
private void setState(TransferEvent.TransferState state)
{
if(currentState != state)
{
if(currentState != null)
{
TransferEventImpl event = new TransferEventEndState();
event.setMessage("End State: " + currentState);
event.setTransferState(state);
queue.add(event);
}
{
TransferEventImpl event = new TransferEventEnterState();
event.setMessage("Enter State: " + state);
event.setTransferState(state);
queue.add(event);
}
currentState = state;
}
}
private void notifyObservers()
{
TransferEvent event = (TransferEvent)queue.poll();
while(event != null)
{
// call the observers
for(TransferCallback callback : observers)
{
callback.processEvent(event);
}
event = (TransferEvent)queue.poll();
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
public interface TransferMessage
{
String getMessage();
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import org.alfresco.service.namespace.QName;
/**
* Transfer Model Constants
*
* @author Mark Rogers
*/
public interface TransferModel
{
static final String TRANSFER_MODEL_1_0_URI = "http://www.alfresco.org/model/transfer/1.0";
static final QName ASPECT_ENABLEABLE = QName.createQName(TRANSFER_MODEL_1_0_URI, "enableable");
// static final QName ASSOC_IMAP_ATTACHMENTS_FOLDER = QName.createQName(IMAP_MODEL_1_0_URI, "attachmentsFolder");
/*
* Type : Transfer Group
*/
static final QName TYPE_TRANSFER_GROUP = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferGroup");
/*
* Type : Transfer Target
*/
static final QName TYPE_TRANSFER_TARGET = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferTarget");
static final QName PROP_ENDPOINT_PROTOCOL = QName.createQName(TRANSFER_MODEL_1_0_URI, "endpointprotocol");
static final QName PROP_ENDPOINT_HOST = QName.createQName(TRANSFER_MODEL_1_0_URI, "endpointhost");
static final QName PROP_ENDPOINT_PORT = QName.createQName(TRANSFER_MODEL_1_0_URI, "endpointport");
static final QName PROP_ENDPOINT_PATH = QName.createQName(TRANSFER_MODEL_1_0_URI, "endpointpath");
static final QName PROP_USERNAME = QName.createQName(TRANSFER_MODEL_1_0_URI, "username");
static final QName PROP_PASSWORD = QName.createQName(TRANSFER_MODEL_1_0_URI, "password");
static final QName PROP_ENABLED = QName.createQName(TRANSFER_MODEL_1_0_URI, "enabled");
/*
* Type : Transfer Lock
*/
static final QName TYPE_TRANSFER_LOCK = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferLock");
static final QName PROP_TRANSFER_ID = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferId");
/*
* Type : Transfer report
*/
static final QName TYPE_TRANSFER_REPORT = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferReport");
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import org.alfresco.service.cmr.transfer.TransferException;
/**
* @author brian
*
*/
public class TransferProcessingException extends TransferException
{
private boolean fatal = false;
/**
* @param msgId
* @param msgParams
* @param cause
*/
public TransferProcessingException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
}
/**
* @param msgId
* @param msgParams
*/
public TransferProcessingException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
}
/**
* @param msgId
* @param cause
*/
public TransferProcessingException(String msgId, Throwable cause)
{
super(msgId, cause);
}
/**
* @param msgId
*/
public TransferProcessingException(String msgId)
{
super(msgId);
}
/**
* @return the fatal
*/
public boolean isFatal()
{
return fatal;
}
/**
* @param fatal the fatal to set
*/
public void setFatal(boolean fatal)
{
this.fatal = fatal;
}
}

View File

@@ -0,0 +1,889 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
import org.alfresco.service.cmr.transfer.TransferEvent;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.jlan.smb.dcerpc.UUID;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transfer.manifest.TestTransferManifestProcessor;
import org.alfresco.repo.transfer.manifest.TransferManifestDeletedNode;
import org.alfresco.repo.transfer.manifest.TransferManifestHeader;
import org.alfresco.repo.transfer.manifest.TransferManifestNode;
import org.alfresco.repo.transfer.manifest.TransferManifestNodeFactory;
import org.alfresco.repo.transfer.manifest.TransferManifestNodeFactoryImpl;
import org.alfresco.repo.transfer.manifest.TransferManifestNodeHelper;
import org.alfresco.repo.transfer.manifest.TransferManifestNormalNode;
import org.alfresco.repo.transfer.manifest.TransferManifestProcessor;
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.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentWriter;
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.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.transfer.TransferCallback;
import org.alfresco.service.cmr.transfer.TransferDefinition;
import org.alfresco.service.cmr.transfer.TransferException;
import org.alfresco.service.cmr.transfer.TransferService;
import org.alfresco.service.cmr.transfer.TransferTarget;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;
public class TransferServiceImpl implements TransferService
{
private static final String MSG_NO_HOME = "transfer_service.unable_to_find_transfer_home";
private static final String MSG_NO_GROUP = "transfer_service.unable_to_find_transfer_group";
private static final String MSG_NO_TARGET = "transfer_service.unable_to_find_transfer_target";
private static final String MSG_TARGET_EXISTS = "transfer_service.target_exists";
private static final String MSG_NO_NODES = "transfer_service.no_nodes";
private static Log logger = LogFactory.getLog(TransferServiceImpl.class);
public void init()
{
PropertyCheck.mandatory(this, "nodeService", nodeService);
PropertyCheck.mandatory(this, "searchService", getSearchService());
PropertyCheck.mandatory(this, "transferSpaceQuery", transferSpaceQuery);
PropertyCheck.mandatory(this, "defaultTransferGroup", defaultTransferGroup);
PropertyCheck.mandatory(this, "transmitter", transmitter);
PropertyCheck.mandatory(this, "namespaceResolver", transmitter);
PropertyCheck.mandatory(this, "actionService", actionService);
PropertyCheck.mandatory(this, "transactionService", transactionService);
}
private String transferSpaceQuery;
private String defaultTransferGroup;
private NodeService nodeService;
private SearchService searchService;
private TransferTransmitter transmitter;
private TransactionService transactionService;
private ActionService actionService;
private TransferManifestNodeFactory transferManifestNodeFactory;
private TransferReporter transferReporter;
/**
* create transfer target
*/
public TransferTarget createTransferTarget(String name, String title, String description, String endpointProtocol, String endpointHost, int endpointPort, String endpointPath, String username, char[] password)
{
/**
* Check whether name is already used
*/
NodeRef dummy = lookupTransferTarget(name);
if(dummy != null)
{
throw new TransferException(MSG_TARGET_EXISTS, new Object[]{name} );
}
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
// type properties
properties.put(TransferModel.PROP_ENDPOINT_HOST, endpointHost);
properties.put(TransferModel.PROP_ENDPOINT_PORT, endpointPort);
properties.put(TransferModel.PROP_ENDPOINT_PROTOCOL, endpointProtocol);
properties.put(TransferModel.PROP_ENDPOINT_PATH, endpointPath);
properties.put(TransferModel.PROP_USERNAME, username);
properties.put(TransferModel.PROP_PASSWORD, encrypt(password));
// titled aspect
properties.put(ContentModel.PROP_TITLE, title);
properties.put(ContentModel.PROP_NAME, name);
properties.put(ContentModel.PROP_DESCRIPTION, description);
// enableable aspect
properties.put(TransferModel.PROP_ENABLED, Boolean.TRUE);
NodeRef home = getTransferHome();
/**
* Work out which group the transfer target is for, in this case the default group.
*/
NodeRef defaultGroup = nodeService.getChildByName(home, ContentModel.ASSOC_CONTAINS, defaultTransferGroup);
/**
* Go ahead and create the new node
*/
ChildAssociationRef ref = nodeService.createNode(defaultGroup, ContentModel.ASSOC_CONTAINS, QName.createQName(TransferModel.TRANSFER_MODEL_1_0_URI, name), TransferModel.TYPE_TRANSFER_TARGET, properties);
/**
* Now create a new TransferTarget object to return to the caller.
*/
TransferTargetImpl newTarget = new TransferTargetImpl();
mapTransferTarget(ref.getChildRef(), newTarget);
return newTarget;
}
/**
* Get all transfer targets
*/
public Set<TransferTarget> getTransferTargets()
{
NodeRef home = getTransferHome();
Set<TransferTarget> ret = new HashSet<TransferTarget>();
// get all groups
List<ChildAssociationRef> groups = nodeService.getChildAssocs(home);
// for each group
for(ChildAssociationRef group : groups)
{
NodeRef groupNode = group.getChildRef();
List<ChildAssociationRef>children = nodeService.getChildAssocs(groupNode, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
for(ChildAssociationRef child : children)
{
if(nodeService.getType(child.getChildRef()).equals(TransferModel.TYPE_TRANSFER_TARGET))
{
TransferTargetImpl newTarget = new TransferTargetImpl();
mapTransferTarget(child.getChildRef(), newTarget);
ret.add(newTarget);
}
}
}
return ret;
}
/**
* Get all transfer targets in the specified group
*/
public Set<TransferTarget> getTransferTargets(String groupName)
{
NodeRef home = getTransferHome();
Set<TransferTarget> ret = new HashSet<TransferTarget>();
// get group with assoc groupName
NodeRef groupNode = nodeService.getChildByName(home, ContentModel.ASSOC_CONTAINS, groupName);
if(groupNode == null)
{
// No transfer group.
throw new TransferException(MSG_NO_GROUP, new Object[]{groupName});
}
/**
* Get children of groupNode
*/
List<ChildAssociationRef>children = nodeService.getChildAssocs(groupNode, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
for(ChildAssociationRef child : children)
{
if(nodeService.getType(child.getChildRef()).equals(TransferModel.TYPE_TRANSFER_TARGET))
{
TransferTargetImpl newTarget = new TransferTargetImpl();
mapTransferTarget(child.getChildRef(), newTarget);
ret.add(newTarget);
}
}
return ret;
}
/**
*
*/
public void deleteTransferTarget(String name)
{
NodeRef nodeRef = lookupTransferTarget(name);
if(nodeRef == null)
{
// target does not exist
throw new TransferException(MSG_NO_TARGET, new Object[]{name} );
}
nodeService.deleteNode(nodeRef);
}
/**
* Enables/Disables the named transfer target
*/
public void enableTransferTarget(String name, boolean enable)
{
NodeRef nodeRef = lookupTransferTarget(name);
nodeService.setProperty(nodeRef, TransferModel.PROP_ENABLED, new Boolean(enable));
}
/**
*
*/
public TransferTarget getTransferTarget(String name)
{
NodeRef nodeRef = lookupTransferTarget(name);
if(nodeRef == null)
{
// target does not exist
throw new TransferException(MSG_NO_TARGET, new Object[]{name} );
}
TransferTargetImpl newTarget = new TransferTargetImpl();
mapTransferTarget(nodeRef, newTarget);
return newTarget;
}
/**
*
*/
public TransferTarget updateTransferTarget(TransferTarget update)
{
NodeRef nodeRef = lookupTransferTarget(update.getName());
if(nodeRef == null)
{
// target does not exist
throw new TransferException(MSG_NO_TARGET, new Object[]{update.getName()} );
}
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
properties.put(TransferModel.PROP_ENDPOINT_HOST, update.getEndpointHost());
properties.put(TransferModel.PROP_ENDPOINT_PORT, update.getEndpointPort());
properties.put(TransferModel.PROP_ENDPOINT_PROTOCOL, update.getEndpointProtocol());
properties.put(TransferModel.PROP_ENDPOINT_PATH, update.getEndpointPath());
properties.put(TransferModel.PROP_USERNAME, update.getUsername());
properties.put(TransferModel.PROP_PASSWORD, encrypt(update.getPassword()));
// titled aspect
properties.put(ContentModel.PROP_TITLE, update.getTitle());
properties.put(ContentModel.PROP_NAME, update.getName());
properties.put(ContentModel.PROP_DESCRIPTION, update.getDescription());
properties.put(TransferModel.PROP_ENABLED, new Boolean(update.isEnabled()));
nodeService.setProperties(nodeRef, properties);
TransferTargetImpl newTarget = new TransferTargetImpl();
mapTransferTarget(nodeRef, newTarget);
return newTarget;
}
/**
*
*/
public NodeRef transfer(String targetName, TransferDefinition definition)
{
final TransferEventProcessor eventProcessor = new TransferEventProcessor();
return transferImpl(targetName, definition, eventProcessor);
}
/**
* Transfer async.
*
* @param targetName
* @param definition
* @param callbacks
*/
public void transferAsync(String targetName, TransferDefinition definition, Set<TransferCallback> callbacks)
{
/**
* Event processor for this transfer instance
*/
final TransferEventProcessor eventProcessor = new TransferEventProcessor();
if(callbacks != null)
{
eventProcessor.observers.addAll(callbacks);
}
Map<String, Serializable> params = new HashMap<String, Serializable>();
params.put("targetName", targetName);
params.put("definition", definition);
params.put("callbacks", (Serializable)callbacks);
Action transferAction = getActionService().createAction("transfer-async", params);
/**
* Execute transfer async in its own transaction.
* The action service only runs actions in the post commit which is why there's
* a separate transaction here.
*/
boolean success = false;
UserTransaction trx = transactionService.getNonPropagatingUserTransaction();
try
{
trx.begin();
logger.debug("calling action service to execute action");
getActionService().executeAction(transferAction, null, false, true);
trx.commit();
logger.debug("committed successfully");
success = true;
}
catch (Exception error)
{
logger.error("unexpected exception", error);
throw new AlfrescoRuntimeException("unable to transfer async", error);
}
finally
{
if(!success)
{
try
{
logger.debug("rolling back after error");
trx.rollback();
}
catch (Exception error)
{
logger.error("unexpected exception during rollback", error);
// There's nothing much we can do here
}
}
}
}
/**
* Transfer Synchronous
* @param targetName
* @param definition
* @param callbacks
*/
public NodeRef transfer(String targetName, TransferDefinition definition, Set<TransferCallback> callbacks)
{
/**
* Event processor for this transfer instance
*/
final TransferEventProcessor eventProcessor = new TransferEventProcessor();
if(callbacks != null)
{
eventProcessor.observers.addAll(callbacks);
}
/**
* Now go ahead and do the transfer
*/
return transferImpl(targetName, definition, eventProcessor);
}
/**
* Transfer Implementation
* @param targetName name of transfer target
* @param definition thranser definition
* @param eventProcessor
*/
private NodeRef transferImpl(String targetName, final TransferDefinition definition, final TransferEventProcessor eventProcessor)
{
NodeRef reportNode = null;
if(logger.isDebugEnabled())
{
logger.debug("transfer started to :" + targetName);
}
/**
* Wire in the transferReport
*/
final List<TransferEvent> transferReport = new LinkedList<TransferEvent>();
TransferCallback reportCallback = new TransferCallback()
{
private static final long serialVersionUID = 4072579605731036522L;
public void processEvent(TransferEvent event)
{
transferReport.add(event);
}
};
eventProcessor.addObserver(reportCallback);
File snapshotFile = null;
TransferTarget target = null;
try
{
target = getTransferTarget(targetName);
// which nodes to write to the snapshot
Set<NodeRef>nodes = definition.getNodes();
if(nodes == null || nodes.size() == 0)
{
logger.debug("no nodes to transfer");
throw new TransferException(MSG_NO_NODES);
}
/**
* create snapshot
*/
String prefix = "TRX-SNAP";
String suffix = ".xml";
long numberOfNodes = 0;
logger.debug("create snapshot");
// where to put snapshot ?
snapshotFile = TempFileProvider.createTempFile(prefix, suffix);
FileWriter snapshotWriter = new FileWriter(snapshotFile);
// Write the manifest file
TransferManifestWriter formatter = new XMLTransferManifestWriter();
TransferManifestHeader header = new TransferManifestHeader();
header.setCreatedDate(new Date());
formatter.startTransferManifest(snapshotWriter);
formatter.writeTransferManifestHeader(header);
for(NodeRef nodeRef : nodes)
{
TransferManifestNode node = transferManifestNodeFactory.createTransferManifestNode(nodeRef);
formatter.writeTransferManifestNode(node);
numberOfNodes++;
}
formatter.endTransferManifest();
snapshotWriter.close();
logger.debug("snapshot file written to local filesystem");
// If we are debugging then write the file to stdout.
if(logger.isDebugEnabled())
{
try
{
outputFile(snapshotFile);
}
catch (Exception error)
{
error.printStackTrace();
}
}
/**
* Begin
*/
eventProcessor.start();
final Transfer transfer = transmitter.begin(target);
if(transfer != null)
{
logger.debug("transfer begin");
boolean prepared = false;
try
{
/**
* send Manifest
*/
eventProcessor.sendManifest(1,1);
DeltaList deltas = transmitter.sendManifest(transfer, snapshotFile);
logger.debug("manifest sent");
/**
* Parse the manifest file and transfer chunks over
*/
// create a chunker and wire it up to the transmitter
final ContentChunker chunker = new ContentChunkerImpl();
final Long fRange = Long.valueOf(numberOfNodes);
chunker.setHandler(
new ContentChunkProcessor(){
private long counter = 0;
public void processChunk(Set<ContentData> data)
{
logger.debug("send chunk to transmitter");
for(ContentData file : data)
{
counter++;
eventProcessor.sendContent(file, fRange, counter);
}
transmitter.sendContent(transfer, data);
}
}
);
// create a manifest processor and wire it up to the chunker
TransferManifestProcessor processor = new TransferManifestProcessor()
{
public void processTransferManifestNode(TransferManifestNormalNode node)
{
Set<ContentData> data = TransferManifestNodeHelper.getContentData(node);
for(ContentData d : data)
{
logger.debug("add content to chunker");
chunker.addContent(d);
}
}
public void processTransferManifiestHeader(TransferManifestHeader header){/* NO-OP */ }
public void startTransferManifest(){ /* NO-OP */ }
public void endTransferManifest(){ /* NO-OP */ }
public void processTransferManifestNode(TransferManifestDeletedNode node)
{ /* NO-OP */
}
};
// wire up the manifest parser to a manifest processor
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser parser;
parser = saxParserFactory.newSAXParser();
XMLTransferManifestReader reader = new XMLTransferManifestReader(processor);
// start the magic
parser.parse(snapshotFile, reader);
chunker.flush();
logger.debug("content sending finished");
/**
* prepare
*/
eventProcessor.prepare();
transmitter.prepare(transfer);
logger.debug("prepared");
/**
* committing
*/
eventProcessor.committing(100, 0);
transmitter.commit(transfer);
/**
* need to poll for status
*/
// transmitter.getCommitStatus(transfer);
eventProcessor.committing(100, 50);
eventProcessor.committing(100, 100);
/**
* committed
*/
//eventProcessor.serverMessage("Committed");
eventProcessor.success();
prepared = true;
logger.debug("committed");
// Write the Successful transfer report if we get here
// in its own transaction so it cannot be rolled back
final TransferTarget fTarget = target;
reportNode = transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
logger.debug("transfer report starting");
NodeRef reportNode = transferReporter.createTransferReport(transfer, fTarget, definition, transferReport);
logger.debug("transfer report done");
return reportNode;
}
});
}
finally
{
if(!prepared)
{
logger.debug("abort incomplete transfer");
transmitter.abort(transfer);
}
}
}
}
catch (TransferException t)
{
eventProcessor.error(t);
/**
* Write the transfer report. This is an error report so needs to be out
*/
if(target != null && reportNode!= null)
{
// reportNode = transferReporter.createTransferReport(target, definition, transferReport);
}
throw t;
}
catch (Exception t)
{
// Wrap any other exception as a transfer exception
logger.debug("unable to transfer", t);
eventProcessor.error(t);
/**
* Write the transfer report. This is an error report so needs to be out
*/
if(target != null && reportNode!= null)
{
// reportNode = transferReporter.createTransferReport(target, definition, transferReport);
}
throw new TransferException("unable to transfer:" + t.toString(), t);
}
finally
{
/**
* clean up
*/
if(snapshotFile != null)
{
snapshotFile.delete();
}
}
return reportNode;
} // end of transferImpl
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public NodeService getNodeService()
{
return nodeService;
}
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
public SearchService getSearchService()
{
return searchService;
}
public void setTransferSpaceQuery(String transferSpaceQuery)
{
this.transferSpaceQuery = transferSpaceQuery;
}
public String getTransferSpaceQuery()
{
return transferSpaceQuery;
}
public void setDefaultTransferGroup(String defaultGroup)
{
this.defaultTransferGroup = defaultGroup;
}
public String getDefaultTransferGroup()
{
return defaultTransferGroup;
}
public TransferTransmitter getTransmitter()
{
return transmitter;
}
public void setTransmitter(TransferTransmitter transmitter)
{
this.transmitter = transmitter;
}
private NodeRef transferHome;
protected NodeRef getTransferHome()
{
if(transferHome == null)
{
String query = transferSpaceQuery;
ResultSet result = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
SearchService.LANGUAGE_XPATH, query);
if(result.length() == 0)
{
// No transfer home.
throw new TransferException(MSG_NO_HOME, new Object[]{query});
}
if (result.getNodeRefs().size() != 0)
{
transferHome = result.getNodeRef(0);
}
}
return transferHome;
}
private char[] encrypt(char[] text)
{
// placeholder dummy implementation - add an 'E' to the start
// String dummy = new String("E" + text);
// String dummy = new String(text);
// return dummy.toCharArray();
return text;
}
private char[] decrypt(char[] text)
{
// placeholder dummy implementation - strips off leading 'E'
// String dummy = new String(text);
return text;
//return dummy.substring(1).toCharArray();
}
/**
*
* @param name
* @return
*/
private NodeRef lookupTransferTarget(String name)
{
String query = "+TYPE:\"trx:transferTarget\" +@cm\\:name:\"" +name + "\"";
ResultSet result = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
SearchService.LANGUAGE_LUCENE, query);
if(result.length() == 1)
{
return result.getNodeRef(0);
}
return null;
}
private void mapTransferTarget(NodeRef nodeRef, TransferTargetImpl def)
{
def.setNodeRef(nodeRef);
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
def.setEndpointPath((String)properties.get(TransferModel.PROP_ENDPOINT_PATH));
def.setEndpointProtocol((String)properties.get(TransferModel.PROP_ENDPOINT_PROTOCOL));
def.setEndpointHost((String)properties.get(TransferModel.PROP_ENDPOINT_HOST));
def.setEndpointPort((Integer)properties.get(TransferModel.PROP_ENDPOINT_PORT));
Serializable passwordVal = properties.get(TransferModel.PROP_PASSWORD);
if(passwordVal.getClass().isArray())
{
def.setPassword(decrypt((char[])passwordVal));
}
if(passwordVal instanceof String)
{
String password = (String)passwordVal;
def.setPassword(decrypt(password.toCharArray()));
}
def.setUsername((String)properties.get(TransferModel.PROP_USERNAME));
def.setName((String)properties.get(ContentModel.PROP_NAME));
def.setTitle((String)properties.get(ContentModel.PROP_TITLE));
def.setDescription((String)properties.get(ContentModel.PROP_DESCRIPTION));
if(nodeService.hasAspect(nodeRef, TransferModel.ASPECT_ENABLEABLE))
{
def.setEnabled((Boolean)properties.get(TransferModel.PROP_ENABLED));
}
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.transfer.TransferService#verify(org.alfresco.service.cmr.transfer.TransferTarget)
*/
public void verify(TransferTarget target) throws TransferException
{
transmitter.verifyTarget(target);
}
/**
* Utility to dump the contents of a file to the console
* @param file
*/
private static void outputFile(File file) throws Exception
{
BufferedReader reader = new BufferedReader(new FileReader(file));
String s = reader.readLine();
while(s != null)
{
System.out.println(s);
s = reader.readLine();
}
}
public void setTransferManifestNodeFactory(TransferManifestNodeFactory transferManifestNodeFactory)
{
this.transferManifestNodeFactory = transferManifestNodeFactory;
}
public TransferManifestNodeFactory getTransferManifestNodeFactory()
{
return transferManifestNodeFactory;
}
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
public ActionService getActionService()
{
return actionService;
}
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
public TransactionService getTransactionService()
{
return transactionService;
}
public void setTransferReporter(TransferReporter transferReporter)
{
this.transferReporter = transferReporter;
}
public TransferReporter getTransferReporter()
{
return transferReporter;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,174 @@
package org.alfresco.repo.transfer;
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.transfer.TransferTarget;
/**
* Data Transfer Object for a TransferTarget. The definition of the connection to a remote system.
*
* @author Mark Rogers
*/
public class TransferTargetImpl implements TransferTarget
{
private NodeRef nodeRef;
private String name;
private String title;
private String description;
private String endpointProtocol;
private String endpointHost;
private int endpointPort;
private String endpointPath;
private String username;
private char[] password;
private boolean enabled;
public void setNodeRef(NodeRef nodeRef)
{
this.nodeRef = nodeRef;
}
public NodeRef getNodeRef()
{
return nodeRef;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setTitle(String title)
{
this.title = title;
}
public String getTitle()
{
return title;
}
public void setDescription(String description)
{
this.description = description;
}
public String getDescription()
{
return description;
}
public void setEndpointProtocol(String endpointProtocol)
{
this.endpointProtocol = endpointProtocol;
}
public String getEndpointProtocol()
{
return endpointProtocol;
}
public void setEndpointHost(String endpointHost)
{
this.endpointHost = endpointHost;
}
public String getEndpointHost()
{
return endpointHost;
}
public void setPassword(char[] password)
{
this.password = password;
}
public char[] getPassword()
{
return password;
}
public void setUsername(String username)
{
this.username = username;
}
public String getUsername()
{
return username;
}
public void setEndpointPath(String endpointPath)
{
this.endpointPath = endpointPath;
}
public String getEndpointPath()
{
return endpointPath;
}
public void setEndpointPort(int endpointPort)
{
this.endpointPort = endpointPort;
}
public int getEndpointPort()
{
return endpointPort;
}
/**
* @see #getNodeRef()
* @see NodeRef#equals(Object)
*/
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
else if (this == obj)
{
return true;
}
else if (obj instanceof TransferTargetImpl == false)
{
return false;
}
TransferTargetImpl that = (TransferTargetImpl) obj;
return (this.getNodeRef().equals(that.getNodeRef()));
}
/**
* @see #getNodeRef()
* @see NodeRef#hashCode()
*/
public int hashCode()
{
return getNodeRef().hashCode();
}
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
public boolean isEnabled()
{
return enabled;
}
public String toString()
{
return "TransferTarget: " + name + ",host:" + endpointHost + ",port:" + endpointPort;
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
import java.io.File;
import java.io.InputStream;
import java.util.Set;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.transfer.TransferException;
import org.alfresco.service.cmr.transfer.TransferTarget;
/**
* @author brian
*
*/
public interface TransferTransmitter
{
/**
* Verify that the target is available
* @param target
* @throws TransferException
*/
void verifyTarget(TransferTarget target) throws TransferException;
/**
* Begin a transfer, the transfer object returned will be used by subsequent
* calls to the transfer service.
*
* @param target definition of where to transfer to.
* @return the transfer object or null if the target cannot be locked.
* @throws TransferException
*/
Transfer begin(TransferTarget target) throws TransferException;
/**
* @param manifest, the transfer manifest file
* @param transfer the transfer object returned by an earlier call to begin
* @return the delta list.
* @throws TransferException
*/
DeltaList sendManifest(Transfer transfer, File manifest) throws TransferException;
/**
* Send the content of the specified urls
*
* @param transfer the transfer object returned by an earlier call to begin
* @param data the content to send
* @throws TransferException
*/
void sendContent(Transfer transfer, Set<ContentData> data);
/**
*
* @param transfer the transfer object returned by an earlier call to begin
* @throws TransferException
*/
void prepare(Transfer transfer) throws TransferException;
/**
* @param transfer the transfer object returned by an earlier call to begin
* @throws TransferException
*/
void commit(Transfer transfer) throws TransferException;
/**
* Abort the transfer
* @param transfer the transfer object returned by an earlier call to begin
* @throws TransferException
*/
void abort(Transfer transfer) throws TransferException;
/**
* Get Async Messages for a transfer.
* Server Side Messages.
* @return messages
*/
Set<TransferMessage> getMessages(Transfer transfer);
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
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.net.MalformedURLException;
import java.net.URL;
import java.util.Set;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.transfer.TransferException;
import org.alfresco.service.cmr.transfer.TransferReceiver;
import org.alfresco.service.cmr.transfer.TransferTarget;
/**
* This class delegates transfer service to the transfer receiver without
* using any networking.
*
* It is used for unit testing the transfer service without requiring two instance
* of the repository to be running.
*
* @author Mark Rogers
*/
public class UnitTestInProcessTransmitterImpl implements TransferTransmitter
{
private TransferReceiver receiver;
private ContentService contentService;
public UnitTestInProcessTransmitterImpl(TransferReceiver receiver, ContentService contentService)
{
this.receiver = receiver;
this.contentService = contentService;
}
public Transfer begin(TransferTarget target) throws TransferException
{
Transfer transfer = new Transfer();
String transferId = receiver.start();
transfer.setTransferId(transferId);
transfer.setTransferTarget(target);
return transfer;
}
public void abort(Transfer transfer) throws TransferException
{
String transferId = transfer.getTransferId();
receiver.abort(transferId);
}
public void commit(Transfer transfer) throws TransferException
{
String transferId = transfer.getTransferId();
receiver.commit(transferId);
}
public Set<TransferMessage> getMessages(Transfer transfer)
{
String transferId = transfer.getTransferId();
return null;
}
public void prepare(Transfer transfer) throws TransferException
{
String transferId = transfer.getTransferId();
receiver.prepare(transferId);
}
public void sendContent(Transfer transfer, Set<ContentData> data)
{
String transferId = transfer.getTransferId();
for(ContentData content : data)
{
String contentUrl = content.getContentUrl();
String fileName = TransferCommons.URLToPartName(contentUrl);
InputStream contentStream = getContentService().getRawReader(contentUrl).getContentInputStream();
receiver.saveContent(transferId, fileName, contentStream);
}
}
public DeltaList sendManifest(Transfer transfer, File manifest) throws TransferException
{
try
{
String transferId = transfer.getTransferId();
FileInputStream fs = new FileInputStream(manifest);
receiver.saveSnapshot(transferId, fs);
}
catch (FileNotFoundException error)
{
throw new TransferException("test error", error);
}
return null;
}
public void verifyTarget(TransferTarget target) throws TransferException
{
}
public void setReceiver(TransferReceiver receiver)
{
this.receiver = receiver;
}
public TransferReceiver getReceiver()
{
return receiver;
}
private void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
private ContentService getContentService()
{
return contentService;
}
}

View File

@@ -0,0 +1,216 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer;
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.TransferManifestNode;
import org.alfresco.repo.transfer.manifest.TransferManifestNodeFactory;
import org.alfresco.repo.transfer.manifest.TransferManifestNormalNode;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
/**
* This is a test class to enable unit testing on a single machine. Since the single machine will already have the
* target node.
*
* @author Mark Rogers
*/
public class UnitTestTransferManifestNodeFactory implements TransferManifestNodeFactory
{
/**
* Map of source to target noderef.
*/
Map<NodeRef, NodeRef> refMap = new HashMap<NodeRef, NodeRef>();
/**
* List of paths
*
* <From Path, To Path>
*/
private List<Pair<Path, Path>> pathMap = new ArrayList<Pair<Path, Path>>();
/**
* The node factory that does the work for real.
*/
TransferManifestNodeFactory realFactory;
/**
* Create a new UnitTestTransferManifestNodeFactory
*
* @param realFactory
*/
UnitTestTransferManifestNodeFactory(TransferManifestNodeFactory realFactory)
{
this.realFactory = realFactory;
}
public TransferManifestNode createTransferManifestNode(NodeRef nodeRef)
{
TransferManifestNode newNode = realFactory.createTransferManifestNode(nodeRef);
NodeRef origNodeRef = newNode.getNodeRef();
/**
* Fiddle with the node ref to prevent a clash with the source
*/
NodeRef mappedNodeRef = mapNodeRef(origNodeRef);
newNode.setNodeRef(mappedNodeRef);
/**
* Fiddle with the parent node ref and parent path.
*/
ChildAssociationRef primaryParentAssoc = newNode.getPrimaryParentAssoc();
NodeRef mappedParentNodeRef = mapNodeRef(primaryParentAssoc.getParentRef());
Path parentPath = newNode.getParentPath();
newNode.setParentPath(getMappedPath(parentPath));
newNode.setPrimaryParentAssoc(new ChildAssociationRef(primaryParentAssoc.getTypeQName(), mappedParentNodeRef,
primaryParentAssoc.getQName(), mappedNodeRef, primaryParentAssoc.isPrimary(),
primaryParentAssoc.getNthSibling()));
/**
* Fiddle with the parent assocs
*/
if (newNode instanceof TransferManifestNormalNode)
{
TransferManifestNormalNode normalNode = (TransferManifestNormalNode) newNode;
List<ChildAssociationRef> mappedParentAssocs = new ArrayList<ChildAssociationRef>();
List<ChildAssociationRef> assocs = normalNode.getParentAssocs();
for (ChildAssociationRef assoc : assocs)
{
ChildAssociationRef replace = new ChildAssociationRef(assoc.getTypeQName(), mappedParentNodeRef,
assoc.getQName(), mappedNodeRef, assoc.isPrimary(), assoc.getNthSibling());
mappedParentAssocs.add(replace);
}
normalNode.setParentAssocs(mappedParentAssocs);
}
/**
* Fiddle with the UUID property
*/
if (newNode instanceof TransferManifestNormalNode)
{
TransferManifestNormalNode normalNode = (TransferManifestNormalNode) newNode;
Map<QName, Serializable> props = normalNode.getProperties();
if (props.containsKey(ContentModel.PROP_NODE_UUID))
{
props.put(ContentModel.PROP_NODE_UUID, mappedNodeRef.getId());
}
}
return newNode;
}
/**
* Get the mapped node ref
*
* @param node
* @return the mapped node ref or null;
*/
public NodeRef getMappedNodeRef(NodeRef node)
{
return refMap.get(node);
}
/**
* Get mapped path
*/
public Path getMappedPath(Path from)
{
Path to = new Path();
/**
*
*/
Path source = new Path();
for (int i = 0; i < from.size(); i++)
{
// Source steps through each element of from.
source.append(from.get(i));
boolean replacePath = false;
for (Pair<Path, Path> xx : getPathMap())
{
// Can't use direct equals because of mismatched node refs (which we don't care about)
if (xx.getFirst().toString().equals(source.toString()))
{
to = xx.getSecond().subPath(xx.getSecond().size() - 1);
replacePath = true;
break;
}
}
if (!replacePath)
{
to.append(from.get(i));
}
}
return to;
}
private NodeRef mapNodeRef(NodeRef in)
{
NodeRef mappedNodeRef = refMap.get(in);
if (mappedNodeRef == null)
{
/**
* Map the node ref by replacing the 36th digit with a Z. The existing UUID could have 0-9 1-F in the 36th
* digit
*/
String nodeRef = in.getId();
if (nodeRef.length() == 36)
{
nodeRef = in.getId().substring(0, 35) + "Z";
}
else
{
nodeRef = in.getId() + "Z";
}
mappedNodeRef = new NodeRef(in.getStoreRef(), nodeRef);
refMap.put(in, mappedNodeRef);
}
return mappedNodeRef;
}
public void setPathMap(List<Pair<Path, Path>> pathMap)
{
this.pathMap = pathMap;
}
public List<Pair<Path, Path>> getPathMap()
{
return pathMap;
}
}

View File

@@ -0,0 +1,272 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.Writer;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.transfer.manifest.TransferManifestHeader;
import org.alfresco.repo.transfer.manifest.TransferManifestNode;
import org.alfresco.repo.transfer.manifest.TransferManifestNodeFactoryImpl;
import org.alfresco.repo.transfer.manifest.TransferManifestWriter;
import org.alfresco.repo.transfer.manifest.XMLTransferManifestWriter;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MLText;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.transfer.TransferException;
import org.alfresco.service.cmr.transfer.TransferService;
import org.alfresco.service.cmr.transfer.TransferTarget;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseAlfrescoSpringTest;
import org.alfresco.util.TempFileProvider;
/**
* Integration test for Transfer Manifest
*
* @author Mark Rogers
*/
public class ManifestIntegrationTest extends BaseAlfrescoSpringTest
{
private TransferService transferService;
private ContentService contentService;
private SearchService searchService;
/**
* Called during the transaction setup
*/
protected void onSetUpInTransaction() throws Exception
{
super.onSetUpInTransaction();
// Get the required services
this.transferService = (TransferService)this.applicationContext.getBean("TransferService");
this.contentService = (ContentService)this.applicationContext.getBean("ContentService");
this.searchService = (SearchService)this.applicationContext.getBean("SearchService");
}
public void testSnapshot() throws Exception
{
// Snapshot a transfer node
String CONTENT_STRING = "hello world";
Locale CONTENT_LOCALE = Locale.TAIWAN;
String CONTENT_TITLE = "the title";
String CONTENT_NAME = "&the name <\\*"; // nasty name for XML
String CONTENT_ASSOC_NAME = "&hell+-1we";
String snapshotMe = "snapshotMe";
String title = "title";
String description = "description";
String endpointProtocol = "http";
String endpointHost = "localhost";
int endpointPort = 8080;
String endpointPath = "rhubarb";
String username = "admin";
char[] password = "password".toCharArray();
Map<NodeRef, TransferManifestNode> sentNodes = new HashMap<NodeRef, TransferManifestNode>();
TransferManifestNodeFactoryImpl nodeFactory = new TransferManifestNodeFactoryImpl();
nodeFactory.setNodeService(nodeService);
/**
* Create our transfer target
*/
TransferTarget target = transferService.createTransferTarget(snapshotMe, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
File snapshotFile = null;
try
{
/**
* Create a test node that we will read and write
*/
ChildAssociationRef child = nodeService.createNode(target.getNodeRef(), ContentModel.ASSOC_CONTAINS, QName.createQName(CONTENT_ASSOC_NAME), ContentModel.TYPE_CONTENT);
NodeRef childNodeRef = child.getChildRef();
ContentWriter writer = contentService.getWriter(childNodeRef, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.putContent(CONTENT_STRING);
nodeService.setProperty(childNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(childNodeRef, ContentModel.PROP_NAME, CONTENT_NAME);
snapshotFile = TempFileProvider.createTempFile("xxx", ".xml");
FileWriter snapshotWriter = new FileWriter(snapshotFile);
Set<NodeRef> nodes = new HashSet<NodeRef>();
nodes.add(nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE));
nodes.add(target.getNodeRef());
nodes.add(childNodeRef);
TransferManifestWriter formatter = new XMLTransferManifestWriter();
TransferManifestHeader header = new TransferManifestHeader();
header.setCreatedDate(new Date());
formatter.startTransferManifest(snapshotWriter);
formatter.writeTransferManifestHeader(header);
for(NodeRef nodeRef : nodes)
{
TransferManifestNode node = nodeFactory.createTransferManifestNode(nodeRef);
formatter.writeTransferManifestNode(node);
sentNodes.put(nodeRef, node);
}
formatter.endTransferManifest();
snapshotWriter.close();
// Show the snapshot file (For dev purposes)
outputFile(snapshotFile);
/**
* Now read the snapshot file
*/
TestTransferManifestProcessor processor = new TestTransferManifestProcessor();
XMLTransferManifestReader reader = new XMLTransferManifestReader(processor);
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser parser = saxParserFactory.newSAXParser();
parser.parse(snapshotFile, reader);
/**
* Now validate that we read back what we write out
*/
assertEquals("did not get back the same number of nodes", nodes.size(), processor.getNodes().size());
assertNotNull("header is null", processor.getHeader());
for(NodeRef nodeId : nodes)
{
System.out.println("Processing node:" + nodeId);
TransferManifestNormalNode readNode = (TransferManifestNormalNode)processor.getNodes().get(nodeId);
TransferManifestNormalNode writeNode = (TransferManifestNormalNode)sentNodes.get(nodeId);
assertNotNull("readNode is null", readNode);
assertNotNull("writeNode is null", writeNode);
assertEquals("type is different", writeNode.getType(), readNode.getType());
assertEquals("nodeRef is different", writeNode.getNodeRef(), readNode.getNodeRef());
assertEquals("parent node ref is different", writeNode.getPrimaryParentAssoc(), readNode.getPrimaryParentAssoc());
if(writeNode.getParentPath() != null)
{
assertEquals("parent path is different", writeNode.getParentPath().toString(), readNode.getParentPath().toString());
}
assertEquals("aspects array different size", writeNode.getAspects().size(), readNode.getAspects().size());
for(QName aspect : writeNode.getAspects())
{
assertTrue("missing aspect", readNode.getAspects().contains(aspect));
}
assertEquals("properties array different size", writeNode.getProperties().size(), readNode.getProperties().size());
for(QName prop : writeNode.getProperties().keySet())
{
assertTrue("missing property", readNode.getProperties().containsKey(prop));
}
assertEquals("child assocs different", writeNode.getChildAssocs().size(), readNode.getChildAssocs().size());
assertEquals("parent assocs different", writeNode.getParentAssocs().size(), readNode.getParentAssocs().size());
assertEquals("source assocs different", writeNode.getSourceAssocs().size(), readNode.getSourceAssocs().size());
assertEquals("target assocs different", writeNode.getTargetAssocs().size(), readNode.getTargetAssocs().size());
if(readNode.getNodeRef().equals(childNodeRef))
{
/**
* Check the child node since we created it at the start of this test this test
*/
ContentData data = (ContentData)readNode.getProperties().get(ContentModel.PROP_CONTENT);
assertEquals("content data wrong size", data.getSize(), CONTENT_STRING.length());
assertEquals("content locale wrong", data.getLocale(), CONTENT_LOCALE);
String childTitle = (String)readNode.getProperties().get(ContentModel.PROP_TITLE);
assertEquals("content title wrong", childTitle, CONTENT_TITLE);
String childName = (String)readNode.getProperties().get(ContentModel.PROP_NAME);
assertEquals("content name wrong", childName, CONTENT_NAME);
/**
* Check the parent associations, there should be only one primary
*/
assertTrue("one parent assoc", readNode.getParentAssocs().size() == 1);
assertTrue("isPrimary", readNode.getParentAssocs().get(0).isPrimary());
assertEquals("parent q name", readNode.getParentAssocs().get(0).getQName(), QName.createQName(CONTENT_ASSOC_NAME));
assertEquals("parent type q name", readNode.getParentAssocs().get(0).getTypeQName(), ContentModel.ASSOC_CONTAINS);
assertEquals("child node ref", readNode.getParentAssocs().get(0).getChildRef(), childNodeRef);
assertEquals("parent node ref", readNode.getParentAssocs().get(0).getParentRef(), readNode.getPrimaryParentAssoc());
assertTrue("zero child assoc", readNode.getChildAssocs().size() == 0);
/**
* Test Node Helper
*/
assertEquals(readNode.getParentAssocs().get(0), TransferManifestNodeHelper.getPrimaryParentAssoc(readNode));
Set<ContentData> content = TransferManifestNodeHelper.getContentData(readNode);
assertEquals("content not found", content.size(), 1);
}
}
}
finally
{
if(snapshotFile != null)
{
snapshotFile.delete();
}
transferService.deleteTransferTarget(snapshotMe);
}
}
/**
* Utility to dump the contents of a file to the console
* @param file
*/
private static void outputFile(File file) throws Exception
{
BufferedReader reader = new BufferedReader(new FileReader(file));
String s = reader.readLine();
while(s != null)
{
System.out.println(s);
s = reader.readLine();
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import org.alfresco.repo.transfer.TransferModel;
import org.alfresco.service.namespace.QName;
/**
* The transfer model - extended for XML Manifest Model
*/
public interface ManifestModel extends TransferModel
{
static final String LOCALNAME_TRANSFER_MAINIFEST = "transferManifest";
static final String LOCALNAME_TRANSFER_HEADER = "transferManifestHeader";
static final String LOCALNAME_HEADER_CREATED_DATE = "createdDate";
static final String LOCALNAME_ELEMENT_NODES = "nodes";
static final String LOCALNAME_ELEMENT_NODE = "node";
static final String LOCALNAME_ELEMENT_DELETED_NODE = "deletedNode";
static final String LOCALNAME_ELEMENT_ASPECTS = "aspects";
static final String LOCALNAME_ELEMENT_ASPECT = "aspect";
static final String LOCALNAME_ELEMENT_PROPERTIES = "properties";
static final String LOCALNAME_ELEMENT_PROPERTY = "property";
static final String LOCALNAME_ELEMENT_PARENT_ASSOCS = "parentAssocs";
static final String LOCALNAME_ELEMENT_CHILD_ASSOCS = "childAssocs";
static final String LOCALNAME_ELEMENT_CHILD_ASSOC = "childAssoc";
static final String LOCALNAME_ELEMENT_PARENT_ASSOC = "parentAssoc";
static final String LOCALNAME_ELEMENT_TARGET_ASSOCS = "targetAssocs";
static final String LOCALNAME_ELEMENT_SOURCE_ASSOCS = "sourceAssocs";
static final String LOCALNAME_ELEMENT_ASSOC = "assoc";
static final String LOCALNAME_ELEMENT_PRIMARY_PARENT = "primaryParent";
static final String LOCALNAME_ELEMENT_PRIMARY_PATH = "primaryPath";
static final String LOCALNAME_ELEMENT_VALUES = "values";
static final String LOCALNAME_ELEMENT_VALUE = "value";
static final String LOCALNAME_ELEMENT_MLVALUE = "mlvalue";
static final String LOCALNAME_ELEMENT_CONTENT_HEADER = "content";
// Manifest file prefix
static final String MANIFEST_PREFIX = "xfer";
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Test implementation of TransferManifestProcessor.
*
* Simply gives access to the data through header and nodes properties.
*
* @author Mark Rogers
*/
public class TestTransferManifestProcessor implements TransferManifestProcessor
{
private TransferManifestHeader header;
private Map<NodeRef, TransferManifestNode> nodes = new HashMap<NodeRef, TransferManifestNode>();
public void endTransferManifest()
{
}
public void processTransferManifestNode(TransferManifestNormalNode node)
{
nodes.put(node.getNodeRef(), node);
}
public void processTransferManifestNode(TransferManifestDeletedNode node)
{
nodes.put(node.getNodeRef(), node);
}
public void processTransferManifiestHeader(TransferManifestHeader header)
{
this.header = header;
}
public void startTransferManifest()
{
}
void setHeader(TransferManifestHeader header)
{
this.header = header;
}
TransferManifestHeader getHeader()
{
return header;
}
void setNodes( Map<NodeRef, TransferManifestNode> nodes)
{
this.nodes = nodes;
}
Map<NodeRef, TransferManifestNode> getNodes()
{
return nodes;
}
}

View File

@@ -0,0 +1,62 @@
package org.alfresco.repo.transfer.manifest;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Path;
/**
* A record of a deleted node in the transfer manifest
*
* The path and node ref refers to the state prior to the node's deletion.
*
* @author Mark Rogers
*/
public class TransferManifestDeletedNode implements TransferManifestNode
{
private NodeRef nodeRef;
private ChildAssociationRef primaryParentAssoc;
private String uuid;
private Path parentPath;
public void setNodeRef(NodeRef nodeRef)
{
this.nodeRef = nodeRef;
}
public NodeRef getNodeRef()
{
return nodeRef;
}
public void setUuid(String uuid)
{
this.uuid = uuid;
}
public String getUuid()
{
return uuid;
}
public void setParentPath(Path parentPath)
{
this.parentPath = parentPath;
}
public Path getParentPath()
{
return parentPath;
}
public void setPrimaryParentAssoc(ChildAssociationRef parentAssoc)
{
this.primaryParentAssoc = parentAssoc;
}
public ChildAssociationRef getPrimaryParentAssoc()
{
return primaryParentAssoc;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import java.util.Date;
/**
* Data value object
*
* Part of the transfer manifest
*/
public class TransferManifestHeader
{
private Date createdDate;
public void setCreatedDate(Date createDate)
{
this.createdDate = createDate;
}
public Date getCreatedDate()
{
return createdDate;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Path;
/**
* Data value object - part of the transfer manifest
*
* Represents a single node in the transfer manifest
*
* @see org.alfresco.repo.transfer.manifest.TransferManifestDeletedNode
* @see org.alfresco.repo.transfer.manifest.TransferManifestNormalNode
*
* @author Mark Rogers
*/
public interface TransferManifestNode
{
public NodeRef getNodeRef();
public void setNodeRef(NodeRef nodeRef);
public void setUuid(String uuid);
public String getUuid();
public void setParentPath(Path parentPath);
public Path getParentPath();
public void setPrimaryParentAssoc(ChildAssociationRef primaryParent);
public ChildAssociationRef getPrimaryParentAssoc();
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import org.alfresco.service.cmr.repository.NodeRef;
public interface TransferManifestNodeFactory
{
TransferManifestNode createTransferManifestNode(NodeRef nodeRef);
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.transfer.TransferException;
import org.alfresco.service.namespace.RegexQNamePattern;
public class TransferManifestNodeFactoryImpl implements TransferManifestNodeFactory
{
private NodeService nodeService;
public void init()
{
}
public TransferManifestNode createTransferManifestNode(NodeRef nodeRef)
{
NodeRef.Status status = nodeService.getNodeStatus(nodeRef);
if(status == null)
{
throw new TransferException("Unable to get node status for node : " + nodeRef);
}
/**
* Work out whether this is a deleted node or not
*/
if(nodeRef.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE) || status.isDeleted())
{
if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_ARCHIVED))
{
// Yes we have an archived aspect
ChildAssociationRef car = (ChildAssociationRef)nodeService.getProperty(nodeRef,
ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC);
TransferManifestDeletedNode node = new TransferManifestDeletedNode();
NodeRef parentNodeRef = car.getParentRef();
node.setNodeRef(car.getChildRef());
node.setPrimaryParentAssoc(car);
if(nodeService.exists(parentNodeRef))
{
// The parent node still exists so it still has a path.
Path parentPath = nodeService.getPath(parentNodeRef);
node.setParentPath(parentPath);
}
return node;
}
// No we don't have an archived aspect - maybe we are not yet committed
TransferManifestDeletedNode node = new TransferManifestDeletedNode();
node.setNodeRef(nodeRef);
ChildAssociationRef parentAssocRef = nodeService.getPrimaryParent(nodeRef);
if(parentAssocRef != null && parentAssocRef.getParentRef() != null)
{
NodeRef parentNodeRef = parentAssocRef.getParentRef();
node.setPrimaryParentAssoc(parentAssocRef);
Path parentPath = nodeService.getPath(parentNodeRef);
node.setParentPath(parentPath);
}
return node;
}
else
{
// This is a "normal" node
TransferManifestNormalNode node = new TransferManifestNormalNode();
node.setNodeRef(nodeRef);
node.setProperties(nodeService.getProperties(nodeRef));
node.setAspects(nodeService.getAspects(nodeRef));
node.setType(nodeService.getType(nodeRef));
ChildAssociationRef parentAssocRef = nodeService.getPrimaryParent(nodeRef);
if(parentAssocRef != null && parentAssocRef.getParentRef() != null)
{
NodeRef parentNodeRef = parentAssocRef.getParentRef();
node.setPrimaryParentAssoc(parentAssocRef);
Path parentPath = nodeService.getPath(parentNodeRef);
node.setParentPath(parentPath);
}
node.setChildAssocs(nodeService.getChildAssocs(nodeRef));
node.setParentAssocs(nodeService.getParentAssocs(nodeRef));
node.setTargetAssocs(nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL ));
node.setSourceAssocs(nodeService.getSourceAssocs(nodeRef, RegexQNamePattern.MATCH_ALL));
return node;
}
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public NodeService getNodeService()
{
return nodeService;
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import java.io.Serializable;
/**
* Decorator to extend capabilities of TransferManifestNode
*
* @author Mark Rogers
*/
public class TransferManifestNodeHelper
{
/**
* Gets the primary parent association
* @param node the node to process
* @return the primary parent association or null if this is a root node
*/
public static ChildAssociationRef getPrimaryParentAssoc(TransferManifestNormalNode node)
{
List<ChildAssociationRef> assocs = node.getParentAssocs();
for(ChildAssociationRef assoc : assocs)
{
if(assoc.isPrimary())
{
return assoc;
}
}
return null;
}
/**
* Gets the content properties for a node
* @param node the node to process
* @return
*/
public static Set<ContentData> getContentData(TransferManifestNormalNode node)
{
Set<ContentData> content = new HashSet<ContentData>();
for(Serializable value : node.getProperties().values())
{
if(value instanceof ContentData)
{
content.add((ContentData)value);
}
}
return content;
}
}

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.namespace.QName;
/**
* Data value object - part of the transfer manifest
*
* Represents a single node and either a create or an update.
*
* @author Mark Rogers
*/
public class TransferManifestNormalNode implements TransferManifestNode
{
private NodeRef nodeRef;
private ChildAssociationRef primaryParentAssoc;
private String uuid;
private QName type;
private Map<QName,Serializable> properties;
private Set<QName> aspects;
private List<ChildAssociationRef> childAssocs;
private List<ChildAssociationRef> parentAssocs;
private List<AssociationRef> sourceAssocs;
private List<AssociationRef> targetAssocs;
private Path parentPath;
public void setNodeRef(NodeRef nodeRef)
{
this.nodeRef = nodeRef;
}
public NodeRef getNodeRef()
{
return nodeRef;
}
public void setUuid(String uuid)
{
this.uuid = uuid;
}
public String getUuid()
{
return uuid;
}
/**
* Gets all properties for the node
*
* @return the properties
*/
public Map<QName,Serializable> getProperties()
{
return properties;
}
//
// /**
// * Gets the property data type
// *
// * @param propertyName name of property
// * @return data type of named property
// */
// public DataTypeDefinition getPropertyDataType(QName propertyName);
//
// /**
// * @return the aspects of this node
// */
// public Set<QName> getNodeAspects();
//
// /**
// * @return true => the node inherits permissions from its parent
// */
// public boolean getInheritPermissions();
//
// /**
// * @return the permissions applied to this node
// */
// public List<AccessPermission> getAccessControlEntries();
public void setProperties(Map<QName,Serializable> properties)
{
this.properties = properties;
}
public void setAspects(Set<QName> aspects)
{
this.aspects = aspects;
}
public Set<QName> getAspects()
{
return aspects;
}
public void setType(QName type)
{
this.type = type;
}
public QName getType()
{
return type;
}
public void setChildAssocs(List<ChildAssociationRef> childAssocs)
{
this.childAssocs = childAssocs;
}
public List<ChildAssociationRef> getChildAssocs()
{
return childAssocs;
}
public void setParentAssocs(List<ChildAssociationRef> parentAssocs)
{
this.parentAssocs = parentAssocs;
}
public List<ChildAssociationRef> getParentAssocs()
{
return parentAssocs;
}
public void setParentPath(Path parentPath)
{
this.parentPath = parentPath;
}
public Path getParentPath()
{
return parentPath;
}
public void setSourceAssocs(List<AssociationRef> sourceAssocs)
{
this.sourceAssocs = sourceAssocs;
}
public List<AssociationRef> getSourceAssocs()
{
return sourceAssocs;
}
public void setTargetAssocs(List<AssociationRef> targetAssocs)
{
this.targetAssocs = targetAssocs;
}
public List<AssociationRef> getTargetAssocs()
{
return targetAssocs;
}
public void setPrimaryParentAssoc(ChildAssociationRef primaryParentAssoc)
{
this.primaryParentAssoc = primaryParentAssoc;
}
public ChildAssociationRef getPrimaryParentAssoc()
{
return primaryParentAssoc;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
/**
* Manifest Processor
*
* Interface called when parsing the transfer manifest file
*
* When Parsing the manifest file, the startTransferManifest will be called first, then
* processHeader, then mulpiple calls of processTransferManifestNode, one for each node,
* then endTransferManifest
*
* @author Mark Rogers
*/
public interface TransferManifestProcessor
{
/**
* Signals the start of a transfer manifest
*/
public void startTransferManifest();
/**
* Gives the header to be proceessed
* @param header, the header
*/
public void processTransferManifiestHeader(TransferManifestHeader header);
/**
* Gives a manifest node to be processed
* @param node, the node
*/
public void processTransferManifestNode(TransferManifestNormalNode node);
/**
* Gives a deleted manifest node to be processed
* @param node, the node
*/
public void processTransferManifestNode(TransferManifestDeletedNode node);
/**
* Signals the end of a transfer manifest
*/
public void endTransferManifest();
}

View File

@@ -0,0 +1,214 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.Reader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.alfresco.service.cmr.dictionary.DictionaryService;
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.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.TempFileProvider;
import junit.framework.TestCase;
public class TransferManifestTest extends TestCase
{
public void testCreateAndReadSnapshot() throws Exception
{
/**
* create snapshot
*/
String prefix = "TRX-SNAP";
String suffix = ".xml";
// where to put snapshot ?
File snapshotFile = TempFileProvider.createTempFile(prefix, suffix);
FileWriter snapshotWriter = new FileWriter(snapshotFile);
// Write the manifest file
TransferManifestWriter formatter = new XMLTransferManifestWriter();
TransferManifestHeader header = new TransferManifestHeader();
header.setCreatedDate(new Date());
formatter.startTransferManifest(snapshotWriter);
formatter.writeTransferManifestHeader(header);
// node to transmit
TransferManifestNormalNode node = new TransferManifestNormalNode();
node.setNodeRef(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "123"));
node.setParentPath(new Path());
Set<QName> aspects = new HashSet<QName>();
aspects.add(QName.createQName("{gsxhjsx}", "cm:wobble"));
aspects.add(QName.createQName("{gsxhjsx}", "cm:wibble"));
node.setAspects(aspects);
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
properties.put(QName.createQName("{gsxhjsx}", "cm:name"), "brian.jpg");
properties.put(QName.createQName("{gsxhjsx}", "cm:created"), new java.util.Date());
properties.put(QName.createQName("{gsxhjsx}", "trx:enabled"), Boolean.FALSE);
MLText mltext = new MLText();
mltext.addValue(Locale.FRENCH, "Bonjour");
mltext.addValue(Locale.ENGLISH, "Hello");
mltext.addValue(Locale.ITALY, "Buongiorno");
properties.put(QName.createQName("{gsxhjsx}", "cm:title"), mltext);
String password = "helloWorld";
properties.put(QName.createQName("{gsxhjsx}", "trx:password"), password.toCharArray());
List<ChildAssociationRef> parents = new ArrayList<ChildAssociationRef>();
ChildAssociationRef primaryParent = new ChildAssociationRef(QName.createQName("{gsxhjsx}", "cm:contains"),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "P1"),
QName.createQName("{gsxhjsx}", "app:smashing"),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "123"),
true,
-1);
parents.add(primaryParent);
parents.add(new ChildAssociationRef(QName.createQName("{gsxhjsx}", "app:wibble"),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "P1"),
QName.createQName("{gsxhjsx}", "app:jskjsdc"),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "123"),
false,
-1));
node.setParentAssocs(parents);
node.setPrimaryParentAssoc(primaryParent);
List<ChildAssociationRef> children = new ArrayList<ChildAssociationRef>();
children.add(new ChildAssociationRef(QName.createQName("{gsxhjsx}", "cm:contains"),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "P1"),
QName.createQName("{gsxhjsx}", "app:super"),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "P5"),
true,
-1));
node.setChildAssocs(children);
Set<String>values=new HashSet<String>();
values.add("red");
values.add("blue");
values.add("green");
properties.put(QName.createQName("{gsxhjsx}", "xyz:colours"), (Serializable)values);
ContentData contentHeader = new ContentData("http://wibble", "mimeType", 123, "utf-8", Locale.ENGLISH);
properties.put(QName.createQName("{gsxhjsx}", "cm:content"), (Serializable)contentHeader);
node.setProperties(properties);
node.setType(QName.createQName("{gsxhjsx}", "trx:nsbbmbs"));
List<AssociationRef> targetAssocs = new ArrayList<AssociationRef>();
List<AssociationRef> sourceAssocs = new ArrayList<AssociationRef>();
targetAssocs.add(new AssociationRef(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "SA"),
QName.createQName("{gsxhjsx}", "app:super"),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "TA")));
sourceAssocs.add(new AssociationRef(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "HH"),
QName.createQName("{gsxhjsx}", "app:super"),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "JJ")));
node.setSourceAssocs(sourceAssocs);
node.setTargetAssocs(targetAssocs);
formatter.writeTransferManifestNode(node);
/**
* Write a second node
*/
TransferManifestNormalNode node2 = new TransferManifestNormalNode();
node2.setNodeRef(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "456"));
node2.setType(QName.createQName("{gsxhjsx}", "trx:dummy"));
formatter.writeTransferManifestNode(node2);
/**
* Write a deleted node
*/
TransferManifestDeletedNode node3 = new TransferManifestDeletedNode();
node3.setNodeRef(new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, "567"));
ChildAssociationRef origPrimaryParent = new ChildAssociationRef(QName.createQName("{gsxhjsx}", "cm:contains"),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "P1"),
QName.createQName("{gsxhjsx}", "app:whopper"),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "567"),
true,
-1);
node3.setPrimaryParentAssoc(origPrimaryParent);
node3.setParentPath(new Path());
formatter.writeTransferManifestNode(node3);
formatter.endTransferManifest();
snapshotWriter.close();
//
BufferedReader reader = new BufferedReader(new FileReader(snapshotFile));
String s = reader.readLine();
while(s != null)
{
System.out.println(s);
s = reader.readLine();
}
// Now try to parse the snapshot file we have just created
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser parser = saxParserFactory.newSAXParser();
TransferManifestProcessor tp = new TestTransferManifestProcessor();
XMLTransferManifestReader xmlReader = new XMLTransferManifestReader(tp);
parser.parse(snapshotFile, xmlReader );
snapshotFile.delete();
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import java.io.Writer;
import java.util.Set;
import org.alfresco.service.cmr.repository.NodeRef;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;
/**
* Transfer Manifest Writer
*
* This class formats the transfer manifest and prints it to the specified writer
*
* It is a statefull object and writes one manifest at a time.
*
* Call start once, then write the header, then one or more nodes, then end.
*
*/
public interface TransferManifestWriter
{
/**
*
* @param writer
* @throws SAXException
*/
void startTransferManifest(Writer writer) throws SAXException;
/**
*
* @param header
* @throws SAXException
*/
void writeTransferManifestHeader(TransferManifestHeader header) throws SAXException;
/**
*
* @param node
* @throws SAXException
*/
void writeTransferManifestNode(TransferManifestNode node) throws SAXException;
/**
*
* @throws SAXException
*/
void endTransferManifest() throws SAXException;
}

View File

@@ -0,0 +1,596 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.transfer.PathHelper;
import org.alfresco.service.cmr.repository.Path;
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.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ISO8601DateFormat;
import org.alfresco.util.ISO9075;
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;
import java.io.Serializable;
/**
* SAX XML Content Handler to read a transfer manifest XML Stream and
* delegate processing to the specified TransferManifestProcessor
*
* @author Mark Rogers
*/
public class XMLTransferManifestReader extends DefaultHandler implements ContentHandler, NamespacePrefixResolver
{
private TransferManifestProcessor processor;
/**
* These are the namespaces used within the document - there may be a different mapping to
* the namespaces of the Data Dictionary.
*/
// Map<String, String> documentNamespaces = new HashMap<String, String>();
final String TRANSFER_URI = ManifestModel.TRANSFER_MODEL_1_0_URI;
final String XMLNS_URI = "http://www.w3.org/XML/1998/namespace";
LinkedList<HashMap<String, String>> namespaces = new LinkedList<HashMap<String, String>>();
public XMLTransferManifestReader(TransferManifestProcessor snapshotProcessor)
{
this.processor = snapshotProcessor;
// prefix to uri map
HashMap<String, String> namespace = new HashMap<String, String>();
namespace.put("xmlns", XMLNS_URI);
namespaces.add(namespace);
}
public void startDocument() throws SAXException
{
processor.startTransferManifest();
}
public void endDocument() throws SAXException
{
processor.endTransferManifest();
}
public void characters(char[] ch, int start, int length) throws SAXException
{
if(buffer != null)
{
buffer.append(ch, start, length);
}
}
/*
* Current State of the parser
*/
private StringBuffer buffer;
private Map<String, Object>props = new HashMap<String, Object>();
/**
* 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(ManifestModel.LOCALNAME_TRANSFER_MAINIFEST))
{
// Good we got this
}
if(elementName.equals(ManifestModel.LOCALNAME_TRANSFER_HEADER))
{
TransferManifestHeader header = new TransferManifestHeader();
props.put("header", header);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_DELETED_NODE))
{
TransferManifestDeletedNode node = new TransferManifestDeletedNode();
NodeRef nodeRef = new NodeRef(atts.getValue("", "nodeRef"));
node.setNodeRef(nodeRef);
props.put("node", node);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_NODE))
{
TransferManifestNormalNode node = new TransferManifestNormalNode();
NodeRef nodeRef = new NodeRef(atts.getValue("", "nodeRef"));
QName type = QName.createQName(atts.getValue("", "nodeType"));
node.setNodeRef(nodeRef);
node.setType(type);
props.put("node", node);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_ASPECTS))
{
TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node");
node.setAspects(new HashSet<QName>());
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_ASPECT))
{
buffer = new StringBuffer();
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_PROPERTIES))
{
TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node");
HashMap<QName, Serializable>properties = new HashMap<QName, Serializable>();
node.setProperties(properties);
props.put("properties", properties);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_PROPERTY))
{
QName name = QName.createQName(atts.getValue("", "name"));
props.put("name", name);
props.remove("values");
props.remove("mltext");
}
if(elementName.equals(ManifestModel.LOCALNAME_HEADER_CREATED_DATE))
{
buffer = new StringBuffer();
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOCS))
{
TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node");
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOCS))
{
TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node");
node.setChildAssocs(new ArrayList<ChildAssociationRef>());
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOC))
{
buffer = new StringBuffer();
NodeRef to = new NodeRef(atts.getValue("", "to"));
QName type = QName.createQName(atts.getValue("", "type"));
Boolean isPrimary = Boolean.parseBoolean(atts.getValue("", "isPrimary"));
props.put("to", to);
props.put("type", type);
props.put("isPrimary", isPrimary);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOC))
{
buffer = new StringBuffer();
NodeRef from = new NodeRef(atts.getValue("", "from"));
QName type = QName.createQName(atts.getValue("", "type"));
Boolean isPrimary = Boolean.parseBoolean(atts.getValue("", "isPrimary"));
props.put("from", from);
props.put("type", type);
props.put("isPrimary", isPrimary);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_TARGET_ASSOCS))
{
TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node");
List<AssociationRef> assocs = new ArrayList<AssociationRef>();
node.setTargetAssocs(assocs);
props.put("assocs", assocs);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_SOURCE_ASSOCS))
{
TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node");
List<AssociationRef> assocs = new ArrayList<AssociationRef>();
node.setSourceAssocs(assocs);
props.put("assocs", assocs);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_ASSOC))
{
NodeRef source = new NodeRef(atts.getValue("", "source"));
NodeRef target = new NodeRef(atts.getValue("", "target"));
QName type = QName.createQName(atts.getValue("", "type"));
props.put("source", source);
props.put("target", target);
props.put("type", type);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_PRIMARY_PARENT))
{
buffer = new StringBuffer();
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_VALUES))
{
Collection<Serializable> values = new ArrayList<Serializable>();
props.put("values", values);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_VALUE))
{
buffer = new StringBuffer();
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_MLVALUE))
{
MLText mltext = (MLText)props.get("mlvalues");
if(mltext == null)
{
mltext = new MLText();
props.put("mlvalues", mltext);
}
String strLocale = (String)atts.getValue("", "locale");
Locale locale = I18NUtil.parseLocale(strLocale);
props.put("locale", locale);
buffer = new StringBuffer();
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_CONTENT_HEADER))
{
String contentURL = (String)atts.getValue("", "contentURL");
String mimetype = (String)atts.getValue("", "mimetype");
String strLocale = (String)atts.getValue("", "locale");
Locale locale = I18NUtil.parseLocale(strLocale);
String encoding = (String)atts.getValue("", "encoding");
String sizeStr = (String)atts.getValue("", "size");
Long size = Long.valueOf(sizeStr);
ContentData contentHeader = new ContentData(contentURL, mimetype, size.longValue(), encoding, locale);
props.put("contentHeader", contentHeader);
}
}
} // startElement
/**
* End Element
*/
@SuppressWarnings("unchecked")
public void endElement(String uri, String localName, String prefixName) throws SAXException
{
namespaces.removeFirst();
QName elementQName = QName.resolveToQName(this, prefixName);
if(elementQName == null)
{
return;
}
if(elementQName.getNamespaceURI().equals(TRANSFER_URI));
{
// This is one of the transfer manifest elements
String elementName = elementQName.getLocalName();
if(elementName.equals(ManifestModel.LOCALNAME_TRANSFER_MAINIFEST))
{
}
if(elementName.equals(ManifestModel.LOCALNAME_TRANSFER_HEADER))
{
TransferManifestHeader header = (TransferManifestHeader)props.get("header");
// User to process the header
processor.processTransferManifiestHeader(header);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_NODE))
{
TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node");
processor.processTransferManifestNode(node);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_DELETED_NODE))
{
TransferManifestDeletedNode node = (TransferManifestDeletedNode)props.get("node");
processor.processTransferManifestNode(node);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_ASPECTS))
{
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_ASPECT))
{
TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node");
node.getAspects().add(QName.createQName(buffer.toString()));
buffer = null;
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_PROPERTIES))
{
// nothing to do
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_PROPERTY))
{
TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node");
QName name = (QName)props.get("name");
Serializable value = (Serializable)props.get("value");
node.getProperties().put(name, value);
}
if(elementName.equals(ManifestModel.LOCALNAME_HEADER_CREATED_DATE))
{
TransferManifestHeader header = (TransferManifestHeader)props.get("header");
header.setCreatedDate(ISO8601DateFormat.parse(buffer.toString()));
buffer = null;
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOCS))
{
// No-op
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOCS))
{
// No-op
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOC))
{
String value = buffer.toString();
QName name = QName.createQName(value);
NodeRef to = (NodeRef)props.get("to");
QName type = (QName) props.get("type");
Boolean isPrimary = (Boolean)props.get("isPrimary");
TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node");
ChildAssociationRef childAssociationRef = new ChildAssociationRef(type, node.getNodeRef(), name, to, isPrimary, -1);
node.getChildAssocs().add(childAssociationRef);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOC))
{
String value = buffer.toString();
QName name = QName.createQName(value);
NodeRef from = (NodeRef)props.get("from");
QName type = (QName) props.get("type");
Boolean isPrimary = (Boolean)props.get("isPrimary");
TransferManifestNode node = (TransferManifestNode)props.get("node");
ChildAssociationRef childAssociationRef = new ChildAssociationRef(type, from, name, node.getNodeRef(), isPrimary, -1);
if (TransferManifestNormalNode.class.isAssignableFrom(node.getClass()))
{
TransferManifestNormalNode normalNode = (TransferManifestNormalNode)node;
List<ChildAssociationRef> parents = normalNode.getParentAssocs();
if (parents == null)
{
parents = new ArrayList<ChildAssociationRef>();
normalNode.setParentAssocs(parents);
}
parents.add(childAssociationRef);
}
if (isPrimary)
{
node.setPrimaryParentAssoc(childAssociationRef);
}
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_TARGET_ASSOCS))
{
//TransferManifestNode node = (TransferManifestNode)props.get("node");
//node.getTargetAssocs().add((AssociationRef)props.get("assoc"));
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_SOURCE_ASSOCS))
{
//TransferManifestNode node = (TransferManifestNode)props.get("node");
//node.getSourceAssocs().add((AssociationRef)props.get("assoc"));
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_ASSOC))
{
NodeRef source = (NodeRef)props.get("source");
NodeRef target = (NodeRef)props.get("target");
QName type = (QName) props.get("type");
List<AssociationRef> assocs = (List<AssociationRef>)props.get("assocs");
AssociationRef assoc = new AssociationRef(source, type, target);
assocs.add(assoc);
props.put("assoc", new AssociationRef(source, type, target));
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_PRIMARY_PATH))
{
TransferManifestNode node = (TransferManifestNode)props.get("node");
String value = buffer.toString();
Path path = PathHelper.stringToPath(value);
node.setParentPath(path);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_VALUES))
{
props.put("value", props.get("values"));
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_VALUE))
{
Collection<Serializable> values = (Collection<Serializable>)props.get("values");
String value = buffer.toString();
if(values != null)
{
values.add(value);
}
else
{
props.put("value", value);
}
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_MLVALUE))
{
MLText mltext = (MLText)props.get("mlvalues");
Locale locale = (Locale)props.get("locale");
String value = buffer.toString();
mltext.addValue(locale, value);
props.put("value", mltext);
}
if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_CONTENT_HEADER))
{
TransferManifestNode node = (TransferManifestNode)props.get("node");
ContentData data = (ContentData)props.get("contentHeader");
props.put("value", data);
}
}
} // 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
}
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;
}
}

View File

@@ -0,0 +1,432 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.manifest;
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.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.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ISO8601DateFormat;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/**
* XMLTransferManifestWriter
*
* Writes the transfer manifest out to XML format.
*
* @author Mark Rogers
*/
public class XMLTransferManifestWriter implements TransferManifestWriter
{
public XMLTransferManifestWriter()
{
}
private XMLWriter writer;
final AttributesImpl EMPTY_ATTRIBUTES = new AttributesImpl();
final String PREFIX = ManifestModel.MANIFEST_PREFIX;
/**
* Start the transfer manifest
*/
public void startTransferManifest(Writer writer) throws SAXException
{
OutputFormat format = OutputFormat.createPrettyPrint();
format.setNewLineAfterDeclaration(false);
format.setIndentSize(3);
format.setEncoding("UTF-8");
this.writer = new XMLWriter(writer, format);
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, ManifestModel.LOCALNAME_TRANSFER_MAINIFEST, PREFIX + ":" + ManifestModel.LOCALNAME_TRANSFER_MAINIFEST, EMPTY_ATTRIBUTES);
}
/**
* End the transfer manifest
*/
public void endTransferManifest() throws SAXException
{
// End Transfer Manifest
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_TRANSFER_MAINIFEST, PREFIX + ":" + ManifestModel.LOCALNAME_TRANSFER_MAINIFEST);
writer.endPrefixMapping(PREFIX);
writer.endDocument();
}
/**
* Write the transfer manifest header
*/
public void writeTransferManifestHeader(TransferManifestHeader header) throws SAXException
{
if(header.getCreatedDate() != null)
{
// Start Header
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_TRANSFER_HEADER, PREFIX + ":" + ManifestModel.LOCALNAME_TRANSFER_HEADER, EMPTY_ATTRIBUTES);
// Created Date
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_HEADER_CREATED_DATE, PREFIX + ":" + ManifestModel.LOCALNAME_HEADER_CREATED_DATE, EMPTY_ATTRIBUTES);
writeDate(header.getCreatedDate());
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_HEADER_CREATED_DATE, PREFIX + ":" + ManifestModel.LOCALNAME_HEADER_CREATED_DATE);
// End Header
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_TRANSFER_HEADER, PREFIX + ":" + ManifestModel.LOCALNAME_TRANSFER_HEADER);
}
}
/**
* Write a deleted node to the manifest file
*
* @param node
* @throws SAXException
*/
public void writeTransferManifestNode(TransferManifestDeletedNode node) throws SAXException
{
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute("uri", "nodeRef", "nodeRef", "String", node.getNodeRef().toString());
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_NODE, PREFIX + ":" +ManifestModel.LOCALNAME_ELEMENT_DELETED_NODE, attributes);
if(node.getPrimaryParentAssoc() != null)
{
writePrimaryParent(node.getPrimaryParentAssoc(), node.getParentPath());
}
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_NODE, PREFIX + ":" +ManifestModel.LOCALNAME_ELEMENT_DELETED_NODE);
}
public void writeTransferManifestNode(TransferManifestNode node) throws SAXException
{
if(node instanceof TransferManifestDeletedNode)
{
TransferManifestDeletedNode node2 = (TransferManifestDeletedNode)node;
writeTransferManifestNode(node2);
}
else if(node instanceof TransferManifestNormalNode)
{
TransferManifestNormalNode node2 = (TransferManifestNormalNode)node;
writeTransferManifestNode(node2);
}
else
{
throw new IllegalArgumentException("Unexpected type" + node.getClass().getName());
}
}
/**
* Write a normal transfer manifest node
* @param nodeRef
* @throws SAXException
*/
public void writeTransferManifestNode(TransferManifestNormalNode node) throws SAXException
{
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute("uri", "nodeRef", "nodeRef", "String", node.getNodeRef().toString());
attributes.addAttribute("uri", "nodeType", "nodeType", "String", formatQName(node.getType()));
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_NODE, PREFIX + ":" +ManifestModel.LOCALNAME_ELEMENT_NODE, attributes);
if(node.getPrimaryParentAssoc() != null)
{
writePrimaryParent(node.getPrimaryParentAssoc(), node.getParentPath());
}
writeAspects(node.getAspects());
writeProperties(node.getProperties());
writeParentAssocs(node.getParentAssocs());
writeChildAssocs(node.getChildAssocs());
writeTargetAssocs(node.getTargetAssocs());
writeSourceAssocs(node.getSourceAssocs());
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_NODE, PREFIX + ":" +ManifestModel.LOCALNAME_ELEMENT_NODE);
}
private void writeProperties(Map<QName, Serializable> properties) throws SAXException
{
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PROPERTIES, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PROPERTIES, EMPTY_ATTRIBUTES);
if(properties != null )
{
for(Entry<QName, Serializable> entry : properties.entrySet())
{
writeProperty(entry.getKey(), entry.getValue());
}
}
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PROPERTIES, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PROPERTIES);
}
private void writeProperty(QName name, Serializable value) throws SAXException
{
{
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "name", "name", "String", formatQName(name));
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PROPERTY, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PROPERTY, attributes);
}
// TODO - Char [] does not seem to be processed correctly
if(value.getClass().isArray())
{
writeValue(value.toString());
}
// Collection
else if(value instanceof ContentData)
{
ContentData data = (ContentData)value;
AttributesImpl dataAttributes = new AttributesImpl();
dataAttributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "contentURL","contentURL", "String", data.getContentUrl());
dataAttributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "mimetype", "mimetype", "String", data.getMimetype());
dataAttributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "size", "size", "String", Long.toString(data.getSize()));
dataAttributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "encoding", "encoding", "String", data.getEncoding());
dataAttributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "locale", "locale", "String", data.getLocale().toString());
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_CONTENT_HEADER, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_CONTENT_HEADER, dataAttributes);
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_CONTENT_HEADER, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_CONTENT_HEADER);
}
// Collection
else if(value instanceof Collection)
{
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_VALUES, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_VALUES, EMPTY_ATTRIBUTES);
int index = 0;
for (Object valueInCollection : (Collection)value)
{
writeValue((Serializable)valueInCollection);
index++;
}
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_VALUES, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_VALUES);
}
else if(value instanceof MLText)
{
MLText mltext = (MLText)value;
for(Entry<Locale, String> entry : mltext.entrySet())
{
writeMLValue(entry.getKey(), entry.getValue());
}
}
else
{
writeValue(value);
}
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PROPERTY, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PROPERTY);
}
private void writeValue(Serializable value) throws SAXException
{
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_VALUE, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_VALUE, EMPTY_ATTRIBUTES);
String strValue = (String)DefaultTypeConverter.INSTANCE.convert(String.class, value);
writer.characters(strValue.toCharArray(), 0, strValue.length());
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_VALUE, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_VALUE);
}
private void writeMLValue(Locale locale, Serializable value) throws SAXException
{
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "locale", "locale", "String", locale.toString());
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_MLVALUE, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_MLVALUE, attributes);
String strValue = (String)DefaultTypeConverter.INSTANCE.convert(String.class, value);
writer.characters(strValue.toCharArray(), 0, strValue.length());
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_MLVALUE, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_MLVALUE);
}
private void writeAspects(Set<QName> aspects) throws SAXException
{
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_ASPECTS, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_ASPECTS, EMPTY_ATTRIBUTES);
if(aspects != null)
{
for(QName aspect : aspects)
{
writeAspect(aspect);
}
}
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_ASPECTS, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_ASPECTS);
}
private void writeAspect(QName aspect) throws SAXException
{
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_ASPECT, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_ASPECT, EMPTY_ATTRIBUTES);
String name=formatQName(aspect);
writer.characters(name.toCharArray(), 0, name.length());
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_ASPECT, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_ASPECT);
}
private void writeDate(Date date) throws SAXException
{
String dates = ISO8601DateFormat.format(date);
writer.characters(dates.toCharArray(), 0, dates.length());
}
private String formatQName(QName qname)
{
return qname.toString();
}
private void writePrimaryParent(ChildAssociationRef parentAssoc, Path parentPath) throws SAXException
{
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PRIMARY_PARENT, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PRIMARY_PARENT, EMPTY_ATTRIBUTES);
writeParentAssoc(parentAssoc);
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PRIMARY_PATH, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PRIMARY_PATH, EMPTY_ATTRIBUTES);
if(parentPath != null)
{
String path = parentPath.toString();
writer.characters(path.toCharArray(), 0, path.length());
}
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PRIMARY_PATH, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PRIMARY_PATH);
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PRIMARY_PARENT, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PRIMARY_PARENT);
}
private void writeParentAssocs(List<ChildAssociationRef> refs) throws SAXException
{
if(refs != null)
{
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOCS, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOCS, EMPTY_ATTRIBUTES);
for(ChildAssociationRef assoc : refs)
{
writeParentAssoc(assoc);
}
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOCS, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOCS);
}
}
private void writeChildAssocs(List<ChildAssociationRef> refs) throws SAXException
{
if(refs != null)
{
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOCS, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOCS, EMPTY_ATTRIBUTES);
for(ChildAssociationRef assoc : refs)
{
writeChildAssoc(assoc);
}
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOCS, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOCS);
}
}
private void writeParentAssoc(ChildAssociationRef assoc) throws SAXException
{
if(assoc != null)
{
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "from", "from", "String", assoc.getParentRef().toString());
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "type", "type", "String", formatQName(assoc.getTypeQName()));
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "type", "isPrimary", "Boolean", assoc.isPrimary()?"true":"false");
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOC, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOC, attributes);
String name= formatQName(assoc.getQName());
writer.characters(name.toCharArray(), 0, name.length());
assoc.isPrimary();
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOC, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_PARENT_ASSOC);
}
}
private void writeChildAssoc(ChildAssociationRef assoc) throws SAXException
{
if(assoc != null)
{
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "to", "to", "String", assoc.getChildRef().toString());
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "type", "type", "String", formatQName(assoc.getTypeQName()));
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "type", "isPrimary", "Boolean", assoc.isPrimary()?"true":"false");
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOC, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOC, attributes);
String name= formatQName(assoc.getQName());
writer.characters(name.toCharArray(), 0, name.length());
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOC, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_CHILD_ASSOC);
}
}
private void writeTargetAssocs(List<AssociationRef> refs) throws SAXException
{
if(refs != null)
{
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_TARGET_ASSOCS, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_TARGET_ASSOCS, EMPTY_ATTRIBUTES);
for(AssociationRef assoc : refs)
{
writeAssoc(assoc);
}
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_TARGET_ASSOCS, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_TARGET_ASSOCS);
}
}
private void writeSourceAssocs(List<AssociationRef> refs) throws SAXException
{
if(refs != null)
{
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_SOURCE_ASSOCS, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_SOURCE_ASSOCS, EMPTY_ATTRIBUTES);
for(AssociationRef assoc : refs)
{
writeAssoc(assoc);
}
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_SOURCE_ASSOCS, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_SOURCE_ASSOCS);
}
}
private void writeAssoc(AssociationRef ref) throws SAXException
{
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "source", "source", "String", ref.getSourceRef().toString());
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "target", "target", "String", ref.getTargetRef().toString());
attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "type", "type", "String", formatQName(ref.getTypeQName()));
writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_ASSOC, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_ASSOC, attributes);
writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, ManifestModel.LOCALNAME_ELEMENT_ASSOC, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_ASSOC);
}
}

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.alfresco.org/model/transferReport/1.0" xmlns:report="http://www.alfresco.org/model/transferReport/1.0" elementFormDefault="qualified">
<!-- XML Schema for the client side transferReport -->
<complexType name="transferReport">
<xs:annotation>
<xs:documentation>
This is the transfer report root tag
</xs:documentation>
</xs:annotation>
<sequence>
<element name="target" type="report:target" maxOccurs="1" minOccurs="1"></element>
<element name="definition" type="report:definition" maxOccurs="1" minOccurs="1"></element>
<element name="events" type="report:events"></element>
</sequence>
</complexType>
<complexType name="target">
<xs:annotation>
<xs:documentation>
The destination of the transfer
</xs:documentation>
</xs:annotation>
<attribute name="name" type="string"></attribute>
<attribute name="endpointPort" type="int"></attribute>
<attribute name="endpointHost" type="string"></attribute>
</complexType>
<complexType name="definition">
</complexType>
<complexType name="events">
<sequence maxOccurs="unbounded" minOccurs="0">
<element name="event" type="report:event"></element>
</sequence>
</complexType>
<complexType name="event">
<attribute name="date" type="dateTime"></attribute>
<!-- message is content -->
</complexType>
</schema>

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.report;
import org.alfresco.repo.transfer.TransferModel;
import org.alfresco.service.namespace.QName;
/**
* The transfer report model - extended for XML Manifest Model
*/
public interface TransferReportModel extends TransferModel
{
static final String LOCALNAME_TRANSFER_REPORT = "transferReport";
static final String LOCALNAME_TRANSFER_TARGET = "target";
static final String LOCALNAME_TRANSFER_DEFINITION = "definition";
static final String LOCALNAME_TRANSFER_EVENTS = "events";
static final String LOCALNAME_TRANSFER_EVENT = "event";
static final String REPORT_PREFIX = "report";
static final String TRANSFER_REPORT_MODEL_1_0_URI = "http://www.alfresco.org/model/transferReport/1.0";
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.report;
import java.util.List;
import org.alfresco.repo.transfer.Transfer;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.transfer.TransferDefinition;
import org.alfresco.service.cmr.transfer.TransferEvent;
import org.alfresco.service.cmr.transfer.TransferTarget;
public interface TransferReporter
{
/**
* Create a transfer report
* @param target the target of the transfer
* @param definition the definition of the transfer
* @param events the transfer events generated by the transfer.
* @return the node ref of the transfer report
*/
NodeRef createTransferReport(Transfer transfer,
TransferTarget target,
TransferDefinition definition,
List<TransferEvent> events);
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.transfer.report;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.transfer.Transfer;
import org.alfresco.repo.transfer.TransferModel;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.transfer.TransferDefinition;
import org.alfresco.service.cmr.transfer.TransferEvent;
import org.alfresco.service.cmr.transfer.TransferTarget;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyCheck;
import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;
public class TransferReporterImpl implements TransferReporter
{
private NodeService nodeService;
private ContentService contentService;
/** Default encoding **/
private static String DEFAULT_ENCODING = "UTF-8";
public void init()
{
PropertyCheck.mandatory(this, "nodeService", nodeService);
PropertyCheck.mandatory(this, "contentService", contentService);
}
/**
* Create a new transfer report
* @return NodeRef the node ref of the new transfer report
*/
public NodeRef createTransferReport(Transfer transfer,
TransferTarget target,
TransferDefinition definition,
List<TransferEvent> events)
{
Map<QName, Serializable> properties = new HashMap<QName, Serializable> ();
String transferId = transfer.getTransferId();
String title = "transfer report: success :" + transferId;
String name = transferId;
String description = "successful transfer report";
properties.put(ContentModel.PROP_NAME, name);
properties.put(ContentModel.PROP_TITLE, title);
properties.put(ContentModel.PROP_DESCRIPTION, description);
ChildAssociationRef ref = nodeService.createNode(target.getNodeRef(), ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), TransferModel.TYPE_TRANSFER_REPORT, properties);
ContentWriter writer = contentService.getWriter(ref.getChildRef(), ContentModel.PROP_CONTENT, true);
writer.setLocale(Locale.getDefault());
writer.setMimetype(MimetypeMap.MIMETYPE_XML);
writer.setEncoding("UTF-8");
//
XMLTransferReportWriter reportWriter = new XMLTransferReportWriter();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(writer.getContentOutputStream()));
try
{
reportWriter.startTransferReport("UTF-8", bufferedWriter);
// Header
reportWriter.writeTarget(target);
reportWriter.writeDefinition(definition);
// Detail
reportWriter.writeTransferEvents(events);
reportWriter.endTransferReport();
return ref.getChildRef();
}
catch (SAXException se)
{
return null;
}
finally
{
try
{
bufferedWriter.close();
}
catch (IOException error)
{
error.printStackTrace();
}
}
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public NodeService getNodeService()
{
return nodeService;
}
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
public ContentService getContentService()
{
return contentService;
}
}

View File

@@ -0,0 +1,119 @@
package org.alfresco.repo.transfer.report;
import java.io.Writer;
import java.util.Date;
import java.util.List;
import org.alfresco.repo.transfer.TransferModel;
import org.alfresco.repo.transfer.manifest.ManifestModel;
import org.alfresco.repo.transfer.manifest.TransferManifestHeader;
import org.alfresco.service.cmr.transfer.TransferDefinition;
import org.alfresco.service.cmr.transfer.TransferEvent;
import org.alfresco.service.cmr.transfer.TransferTarget;
import org.alfresco.util.ISO8601DateFormat;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
public class XMLTransferReportWriter
{
public XMLTransferReportWriter()
{
}
private XMLWriter writer;
final AttributesImpl EMPTY_ATTRIBUTES = new AttributesImpl();
final String PREFIX = TransferReportModel.REPORT_PREFIX;
/**
* Start the transfer report
*/
public void startTransferReport(String encoding, Writer writer) throws SAXException
{
OutputFormat format = OutputFormat.createPrettyPrint();
format.setNewLineAfterDeclaration(false);
format.setIndentSize(3);
format.setEncoding(encoding);
this.writer = new XMLWriter(writer, format);
this.writer.startDocument();
this.writer.startPrefixMapping(PREFIX, TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI);
// Start Transfer Manifest // uri, name, prefix
this.writer.startElement(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, TransferReportModel.LOCALNAME_TRANSFER_REPORT, PREFIX + ":" + TransferReportModel.LOCALNAME_TRANSFER_REPORT, EMPTY_ATTRIBUTES);
}
/**
* End the transfer report
*/
public void endTransferReport() throws SAXException
{
// End Transfer Manifest
writer.endElement(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, TransferReportModel.LOCALNAME_TRANSFER_REPORT, PREFIX + ":" + TransferReportModel.LOCALNAME_TRANSFER_REPORT);
writer.endPrefixMapping(PREFIX);
writer.endDocument();
}
/**
* Write the target to the report
*/
public void writeTarget(TransferTarget target) throws SAXException
{
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, "name", "name", "String", target.getName());
attributes.addAttribute(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, "endpointHost", "endpointHost", "String", target.getEndpointHost());
attributes.addAttribute(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, "endpointPort", "endpointPort", "int", String.valueOf(target.getEndpointPort()));
writer.startElement(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, TransferReportModel.LOCALNAME_TRANSFER_TARGET, PREFIX + ":" + TransferReportModel.LOCALNAME_TRANSFER_TARGET, attributes);
writer.endElement(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, TransferReportModel.LOCALNAME_TRANSFER_TARGET, PREFIX + ":" + TransferReportModel.LOCALNAME_TRANSFER_TARGET);
}
/**
* Write the definition to the report
*/
public void writeDefinition(TransferDefinition definition) throws SAXException
{
writer.startElement(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, TransferReportModel.LOCALNAME_TRANSFER_DEFINITION, PREFIX + ":" + TransferReportModel.LOCALNAME_TRANSFER_DEFINITION, EMPTY_ATTRIBUTES);
writer.endElement(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, TransferReportModel.LOCALNAME_TRANSFER_DEFINITION, PREFIX + ":" + TransferReportModel.LOCALNAME_TRANSFER_DEFINITION);
}
/**
* Write the transfer manifest header
*/
public void writeTransferEvents(List<TransferEvent> events) throws SAXException
{
writer.startElement(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, TransferReportModel.LOCALNAME_TRANSFER_EVENTS, PREFIX + ":" + TransferReportModel.LOCALNAME_TRANSFER_EVENTS, EMPTY_ATTRIBUTES);
for(TransferEvent event : events)
{
writeTransferEvent(event);
}
writer.endElement(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, TransferReportModel.LOCALNAME_TRANSFER_EVENTS, PREFIX + ":" + TransferReportModel.LOCALNAME_TRANSFER_EVENTS);
}
/**
* Write the transfer manifest header
*/
public void writeTransferEvent(TransferEvent event) throws SAXException
{
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, "date", "date", "dateTime", ISO8601DateFormat.format(event.getTime()));
writer.startElement(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, TransferReportModel.LOCALNAME_TRANSFER_EVENT, PREFIX + ":" + TransferReportModel.LOCALNAME_TRANSFER_EVENT, attributes);
String message = event.getMessage();
if(message != null)
{
writer.characters(message.toCharArray(), 0, message.length());
}
writer.endElement(TransferReportModel.TRANSFER_REPORT_MODEL_1_0_URI, TransferReportModel.LOCALNAME_TRANSFER_EVENT, PREFIX + ":" + TransferReportModel.LOCALNAME_TRANSFER_EVENT);
}
}

View File

@@ -333,4 +333,17 @@ public class ContentData implements Serializable
{ {
return locale; return locale;
} }
/**
* @return hashCode
*/
public int hashCode()
{
if(contentUrl!= null)
{
return contentUrl.hashCode();
}
return 0;
}
} }

View File

@@ -41,7 +41,7 @@ import org.alfresco.util.ISO9075;
* /x/y/z * /x/y/z
* </pre></b> * </pre></b>
* In the above example, there will be <b>4</b> elements, the first being a reference * In the above example, there will be <b>4</b> elements, the first being a reference
* to the root node, followed by qname elements for <b>x</b>, <b>x</b> and <b>z</b>. * to the root node, followed by qname elements for <b>x</b>, <b>y</b> and <b>z</b>.
* <p> * <p>
* Methods and constructors are available to construct a <code>Path</code> instance * Methods and constructors are available to construct a <code>Path</code> instance
* from a path string or by building the path incrementally, including the ability to * from a path string or by building the path incrementally, including the ability to

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* @author brian
*
*/
public interface NodeFilter
{
/**
* Examines the supplied node and indicates whether it has been accepted by the filter.
* @param thisNode
* @param serviceRegistry
* @return true if the supplied node matches the criteria specified on this filter, and false
* otherwise.
*/
boolean accept(NodeRef thisNode);
void init();
void setServiceRegistry(ServiceRegistry serviceRegistry);
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import java.util.Set;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* @author brian
*
*/
public interface NodeFinder
{
/**
* @param thisNode
* @param serviceRegistry
* @return
*/
Set<NodeRef> findFrom(NodeRef thisNode);
void init();
void setServiceRegistry(ServiceRegistry serviceRegistry);
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
/**
* A Ranged Transfer event is a detail record for a State that has many smaller steps. For example when sending content the first
* event is 1 of the number of files to send. The second is 2 of the number of files to send.
*
* These events are intended to support "progress bar" types of interfaces.
*
* @author Mark Rogers
*/
public interface RangedTransferEvent extends TransferEvent
{
/**
* The position in the range
* @return
*/
long getPosition();
/**
* The maximum range
* @return
*/
long getRange();
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import java.io.Serializable;
/**
* The transfer callback is called during a transfer, it allows the real-time feedback of
* an in progress transfer. It can be used to populate a deployment report or to display
* a user interface.
*
* @author Mark Rogers
*/
public interface TransferCallback extends Serializable
{
/**
* processEvent
* @param event
*/
public void processEvent(TransferEvent event);
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import java.io.Serializable;
import java.util.Set;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Definition of a transfer.
*
* Specifies which node to transfer
*
*/
public class TransferDefinition implements Serializable
{
/**
*
*/
private static final long serialVersionUID = -8497919749300106861L;
// Which nodes to deploy
private Set<NodeRef> nodes;
/**
* Set which nodes to transfer
* @param nodes
*/
public void setNodes(Set<NodeRef> nodes)
{
this.nodes = nodes;
}
/**
* Get which nodes to transfer
* @return
*/
public Set<NodeRef> getNodes()
{
return nodes;
}
}

View File

@@ -0,0 +1,38 @@
package org.alfresco.service.cmr.transfer;
import java.util.Date;
/**
* @author Mark Rogers
*/
public interface TransferEvent
{
/**
* The transfer events will Start with a START event and finish with either SUCCESS or ERROR
*/
enum TransferState { START, SENDING_MANIFEST, SENDING_CONTENT, PREPARING, COMMITTING, SUCCESS, ERROR };
/**
* Get the state of this transfer
* @return the state of this transfer
*/
TransferState getTransferState();
/**
* The time this event occured.
* @return the date/time the event
*/
Date getTime();
/**
* Get a human readable message for this event
* @return
*/
String getMessage();
/**
* Is this the last event for this transfer ?
*/
boolean isLast();
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import org.alfresco.repo.transfer.TransferEventImpl;
/**
* TransferEventCommittingStatus are produced when a transfer is being committed.
*
* The range can be used to produce a "progress bar"
*
*/
public class TransferEventCommittingStatus extends TransferEventImpl implements RangedTransferEvent
{
public String toString()
{
return "TransferEventCommittingStatus: " + super.getPosition() + " of " + super.getRange();
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import org.alfresco.repo.transfer.TransferEventImpl;
public class TransferEventEndState extends TransferEventImpl implements TransferEvent
{
public String toString()
{
return ("TransferEventEndState: " + super.toString());
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import org.alfresco.repo.transfer.TransferEventImpl;
public class TransferEventEnterState extends TransferEventImpl implements TransferEvent
{
public String toString()
{
return ("TransferEventEnterState: " + super.toString());
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import org.alfresco.repo.transfer.TransferEventImpl;
/**
* Indicates the reason why a transfer failed
*/
public class TransferEventError extends TransferEventImpl implements TransferEvent
{
private Exception exception;
public void setException(Exception exception)
{
this.exception = exception;
}
public Exception getException()
{
return exception;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import org.alfresco.repo.transfer.TransferEventImpl;
/**
* Ranged Transfer event for sending content (e.g. sending content 1 of 64)
*/
public class TransferEventSendingContent extends TransferEventImpl implements RangedTransferEvent
{
private long size;
public String toString()
{
return "TransferEventSendingContent: " + super.getPosition() + " of " + super.getRange();
}
public void setSize(long size)
{
this.size = size;
}
public long getSize()
{
return size;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import org.alfresco.repo.transfer.TransferEventImpl;
/**
*
*
*/
public class TransferEventSendingManifest extends TransferEventImpl implements RangedTransferEvent
{
public String toString()
{
return "TransferEventSendingManifest: " + super.getPosition() + " of " + super.getRange();
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import org.alfresco.repo.transfer.TransferEventImpl;
public class TransferEventSentContent extends TransferEventImpl implements TransferEvent
{
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import org.alfresco.repo.transfer.TransferEventImpl;
/**
* The success event indicates a successfull transfer
*/
public class TransferEventSuccess extends TransferEventImpl implements TransferEvent
{
public String toString()
{
return "TransferEventSuccess";
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Transfer service exception class
*
* @author Mark Rogers
*/
public class TransferException extends AlfrescoRuntimeException
{
/**
* Serial version UID
*/
private static final long serialVersionUID = 3257571685241467958L;
public TransferException(String msgId)
{
super(msgId);
}
public TransferException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
}
public TransferException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
}
public TransferException(String msgId, Throwable cause)
{
super(msgId, cause);
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import java.io.File;
import java.io.InputStream;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* @author brian
*
*/
public interface TransferReceiver
{
/**
*
* @param transferId
* @return
*/
File getStagingFolder(String transferId);
/**
*
* @param transferId
* @return
*/
NodeRef getTempFolder(String transferId);
/**
* Asks the receiver to setup a new transfer.
* @return The identifier of the new transfer
* @throws TransferException if an error occurred while setting up the transfer
*/
String start() throws TransferException;
/**
* Asks the receiver to end (and clean up) the specified transfer
* @param transferId The transfer to end
* @throws TransferException If the process of ending the transfer fails
*/
void end(String transferId) throws TransferException;
/**
* Nudge the transfer lock (to prevent it expiring) if the supplied transferId matches that referenced by the lock.
* @param transferId
* @throws TransferException if the lock doesn't exist or doesn't correspond to the supplied transferId.
*/
void nudgeLock(String transferId) throws TransferException;
/**
* Store the specified snapshot file into the transfer staging area.
* The specified transfer must currently be the holder of the transfer lock, otherwise an exception is thrown.
* This operation does not close the supplied stream, so the caller must do it as appropriate. The caller
* should assume that the supplied stream has been fully read when this operation returns.
* @param transferId The identifier of the transfer with which this snapshot is associated
* @param snapshotStream The open stream that holds the snapshot file.
* @throws TransferException If an error occurs while saving the snapshot file.
*/
void saveSnapshot(String transferId, InputStream snapshotStream) throws TransferException;
void saveContent(String transferId, String contentId, InputStream contentStream) throws TransferException;
/**
* Prepare
* @param transferId
* @throws TransferException
*/
void prepare(String transferId) throws TransferException;
/**
* Abort
* @param transferId
* @throws TransferException
*/
void abort(String transferId) throws TransferException;
/**
* Commit
* @param transferId
* @throws TransferException
*/
void commit(String transferId) throws TransferException;
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright (C) 2009-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.transfer;
import java.util.Set;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* The transfer service is responsible for transfering nodes between one instance of Alfresco and another remote instance.
* as well as the transfer method, this interface also provides methods for managing the
*
* @author Mark Rogers
*/
public interface TransferService
{
/**
* Transfer nodes, sync. This synchronous version of the transfer method waits for the transfer to complete
* before returning to the caller. Callbacks are called in the current thread context, so will be associated with the current
* transaction and user.
*
* @param targetName the name of the target to transfer to
* The following properties must be set, nodes
* @param definition, the definition of the transfer. Specifies which nodes to transfer.
* @throws TransferException
* @return the node reference of the transfer report
*/
public NodeRef transfer(String targetName, TransferDefinition definition) throws TransferException;
/**
* Transfer nodes sync, with callback. This synchronous version of the transfer method waits for the transfer to complete
* before returning to the caller. Callbacks are called in the current thread context, so will be associated with the current
* transaction and user.
*
* @param targetName the name of the target to transfer to
* @param definition - the definition of the transfer. Specifies which nodes to transfer.
* The following properties must be set, nodes
* @param callback - a set of callback handlers that will be called as transfer proceeds. May be null.
* @throws TransferException
* @return the node reference of the transfer report
*/
public NodeRef transfer(String targetName, TransferDefinition definition, Set<TransferCallback> callback) throws TransferException;
/**
* Transfer nodes async with callback. The asynchronous version of the transfer method starts a transfer and returns as
* soon as possible.
* The transfer callbacks will be called by a different thread to that used to call the transferAsync method so transaction
* context will be different to the calling context.
*
* Please also be aware that the asychronous transfer does not have access to uncommitted
* data in the calling transaction.
*
* @param targetName the name of the target to transfer to
* @param definition - the definition of the transfer. Specifies which nodes to transfer.
* The following properties must be set, nodes
* @param callback - a set of callback handlers that will be called as transfer proceeds. May be null.
* @throws TransferException
*/
public void transferAsync(String targetName, TransferDefinition definition, Set<TransferCallback> callback) throws TransferException;
/**
* Verify a target is available and that the configured credentials correctly identify an admin user.
* @throws TransferException
*/
public void verify(TransferTarget target) throws TransferException;
/**
* crate a new transfer target
* @param name, the name of this transfer target, which must be unique
* @param title, the display name of this transfer target
* @param description,
* @param endpointProtocol, either http or https
* @param endpointHost,
* @param endpointPort,
* @param endpointPath,
* @param username,
* @param password,
*/
public TransferTarget createTransferTarget(String name, String title, String description, String endpointProtocol, String endpointHost, int endpointPort, String endpointPath, String username, char[] password) throws TransferException;
/**
* Get all the transfer targets
*/
public Set<TransferTarget>getTransferTargets() throws TransferException;
/**
* Get All the transfer targets for a particular transfer target group.
* @param groupName, the name of the transfer group
*/
public Set<TransferTarget>getTransferTargets(String groupName) throws TransferException;
/**
* Get a transfer target by its name
* @throws TransferException - target does not exist
*/
public TransferTarget getTransferTarget(String name) throws TransferException;
/**
* Delete a transfer target. After calling this method the transfer target will no longer exist.
* @throws TransferException - target does not exist
* @param name, the name of this transfer target,
*/
public void deleteTransferTarget(String name) throws TransferException;
/**
* Update TransferTarget
* The following properties may be updated:
* endpointHost,
* endpointPort,
* endpointProtocol,
* endpointPath,
* username,
* password,
* title,
* description
*
* The following properties may not be updated:
* name, must be specified.
* nodeRef, if specified will be ignored.
*
* @param update
*/
public TransferTarget updateTransferTarget(TransferTarget update) throws TransferException;
/**
* Enables/Disables the named transfer target
* @param name the name of the transfer target
* @param enable (or false=disable)
*/
public void enableTransferTarget(String name, boolean enable) throws TransferException;
}

View File

@@ -0,0 +1,135 @@
package org.alfresco.service.cmr.transfer;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Transfer Target.
*
* @author Mark Rogers
*/
public interface TransferTarget
{
/**
* read only - get the node reference of the underlying transfer target node.
* @return
*/
public NodeRef getNodeRef();
/**
* Get the name of this transfer target
* @return
*/
public String getName();
/**
* Set the name of this transfer target. Please note that you can't update the name of a
* transfer target.
*
* @param name
*/
public void setName(String name);
/**
* Get the description for this transfer target
* @return
*/
public String getDescription();
/**
* Set the decription for this transfer target
* @param description
*/
public void setDescription(String description);
/**
* Get the title of this transfer target
* @return
*/
String getTitle();
/**
* Set the title for this transfer target
* @param title
*/
public void setTitle(String title);
/**
* Get the endpoint host
* @return
*/
public String getEndpointHost();
/**
* Set the endpoint host
* @param endpointHost
*/
public void setEndpointHost(String endpointHost);
/**
* Get the endpoint port
* @return
*/
int getEndpointPort();
/**
* Set the endpoint port
* @param endpointPort
*/
public void setEndpointPort(int endpointPort);
/**
* HTTP OR HTTPS
*/
public String getEndpointProtocol();
/**
* Set the endpoint protocol.
* @param endpointProtocol
*/
public void setEndpointProtocol(String endpointProtocol);
/**
* Set the password for this transfer target
* @param password clear text password.
*/
public void setPassword(char[] password);
/**
* The username used to authenticate with the transfer target
* @return
*/
String getUsername();
/**
* The username used to authenticate with the transfer target
* @param userName
*/
void setUsername(String userName);
/**
* Get the cleartext password
* @return
*/
char[] getPassword();
/**
* The location of the transfer service on the target endpoint host
* @return
*/
String getEndpointPath();
/**
* The location of the transfer service on the target endpoint host
*/
void setEndpointPath(String path);
/**
* is this transfer target enabled or disabled?
*/
boolean isEnabled();
/**
* enable this transfer target
*/
void setEnabled(boolean enabled);
}

View File

@@ -58,8 +58,10 @@ public final class QName implements QNamePattern, Serializable, Cloneable, Compa
/** /**
* Create a QName * Create a QName
* *
* (With no prefix)
*
* @param namespaceURI the qualifying namespace (maybe null or empty string) * @param namespaceURI the qualifying namespace (maybe null or empty string)
* @param localName the qualified name * @param localName the local name
* @return the QName * @return the QName
*/ */
public static QName createQName(String namespaceURI, String localName) public static QName createQName(String namespaceURI, String localName)
@@ -109,7 +111,7 @@ public final class QName implements QNamePattern, Serializable, Cloneable, Compa
/** /**
* Create a QName * Create a QName (from prefix format) <code>prefix:localName</code>
* *
* @param qname qualified name of the following format <code>prefix:localName</code> * @param qname qualified name of the following format <code>prefix:localName</code>
* @param prefixResolver lookup to resolve mappings between prefix and namespace * @param prefixResolver lookup to resolve mappings between prefix and namespace

View File

@@ -24,6 +24,7 @@
*/ */
package org.alfresco.util; package org.alfresco.util;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.RejectedExecutionHandler;
@@ -191,9 +192,11 @@ public class ThreadPoolExecutorFactoryBean implements FactoryBean, InitializingB
if (workQueueSize < 0) if (workQueueSize < 0)
{ {
workQueueSize = Integer.MAX_VALUE; // Setting workQueueSize to MAX_VALUE prevents the pool growing and shrinking. See JavaDoc
// workQueueSize = Integer.MAX_VALUE;
workQueueSize = 1000;
} }
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(workQueueSize); BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(workQueueSize);
// construct the instance // construct the instance
instance = new ThreadPoolExecutor( instance = new ThreadPoolExecutor(