mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merge from BRANCHES/DEV/CLOUD1_SPRINT1 to HEAD:
40238: CLOUD-37 - Initial Commit to test Merged BRANCHES/DEV/AMILLER/CLOUD1_SPRINT1 to BRANCHES/DEV/CLOUD1_SPRINT1: 40077: CLOUD-37: Initial commit. 40101: CLOUD-37: Fix build error. 40114: CLOUD-37: Fix path names and missing files. 40122: CLOUD-37: Initial drop of UI code for investigation of progress issues 40124: CLOUD-37: A couple of minor UI tweaks (set icon and hide panel before archive download) 40125: CLOUD-37: Download files and folders as zip 40134: CLOUD-37: Updates to UI (javascript doc, CSS tweaks, intervals for requests, labels, etc). 40143: CLOUD-37: Error messages for failures, more JavaScript doc, archive naming, code tidy 40157: CLOUD-37 - Download files and folders as zip 40202: CLOUD-37: UI tweaks following UX review 40217: CLOUD-37: Add file count to status reports. 40222: CLOUD-37: Added information to download dialog to report on the number of files added to the zip 40240: CLOUD-37: Remove extraneous file, breaking build 40513: CLOUD-37: Add Action Service Metrics Merged BRANCHES/DEV/AMILLER/CLOUD1_SPRINT1 to BRANCHES/DEV/CLOUD1_SPRINT1: 40260: CLOUD-37: Add action service metrics 40309: CLOUD-37: Fix JMX configuration, pointing at renamed class. 40514: CLOUD-37: Enable the execution of the zip creation process on a remote transformation node Merged BRANCHES/DEV/AMILLER/CLOUD1_SPRINT1 to BRANCHES/DEV/CLOUD1_SPRINT1: 40369: CLOUD-37: Enable the execution of the zip creation process on a remote transformation node 40516: CLOUD-37: Implement clean up job. Merged BRANCHES/DEV/AMILLER/CLOUD1_SPRINT1 to BRANCHES/DEV/CLOUD1_SPRINT1: 40462: CLOUD-37: Implement clean up job. 40517: CLOUD-505: Add entries for folders. Merged BRANCHES/DEV/AMILLER/CLOUD1_SPRINT1 to BRANCHES/DEV/CLOUD1_SPRINT1: 40493: CLOUD-505: Add entries for folders. 40547: CLOUD-37: Fix broken test 40595: CLOUD-518: Add working copy/locked file filtering 40642: CLOUD-508: Prevent problems occurring when cancelling and restarting the same download 40643: CLOUD-507: When a single item is selected for download it the item name gets used for the archive name 41442: CLOUD-590: Limit the total size of the content which can be downloaded. This can be set via the property, download.maxContentSize. The default is 2GB. 41472: CLOUD-589: Added cancelled flag to download type and added checks in Zip creation action to act upon the setting of this flag. Also added webscript for canceling the download. 41692: Adds support to Alfresco.util.formatFileSize for file sizes with commas (as needed by zip download) 41693: Zip Download enhancements: CLOUD-590: Notifies the user when they've exceeded the maximum file size limit. CLOUD-626: Better handling when there are errors during zipping. (WIP) 41713: Zip Download Updates: CLOUD-589: A cancel download UI action now triggers a delete of the archive on the server. CLOUD-626: The UI now triggers a full download cancel (with node delete) in event of an error. 41737: Updates Alfresco.util.formatFileSize to support an optional decimal places param. (For CLOUD-685) 41739: CLOUD-685: Display total file size of files for download to two decimal places when there is an error. 41832: Fixes: CLOUD-704: new CANCELLED status is now handled correctly. 41887: CLOUD-686: Updated maximum download content size to 2152852358 bytes (2.005GB) 41965: CLOUD-703: Upload content now runs as system user, and Quota Service returns unlimited quota for system user. 42025: CLOUD-703: Fix test failures and ensure S3 content store works in the clustered and non-clustered environments git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@42146 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -95,6 +95,9 @@
|
|||||||
<property name="dictionaryService">
|
<property name="dictionaryService">
|
||||||
<ref bean="DictionaryService" />
|
<ref bean="DictionaryService" />
|
||||||
</property>
|
</property>
|
||||||
|
<property name="monitor">
|
||||||
|
<ref bean="actionServiceMonitor"/>
|
||||||
|
</property>
|
||||||
|
|
||||||
<property name="asynchronousActionExecutionQueues">
|
<property name="asynchronousActionExecutionQueues">
|
||||||
<map>
|
<map>
|
||||||
@@ -125,6 +128,9 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- Running Actions monitor -->
|
||||||
|
<bean id="actionServiceMonitor" class="org.alfresco.repo.action.ActionServiceMonitor"/>
|
||||||
|
|
||||||
<!-- JavaScript API support for action tracking -->
|
<!-- JavaScript API support for action tracking -->
|
||||||
<bean id="actionTrackingServiceScript" parent="baseJavaScriptExtension"
|
<bean id="actionTrackingServiceScript" parent="baseJavaScriptExtension"
|
||||||
class="org.alfresco.repo.action.script.ScriptActionTrackingService">
|
class="org.alfresco.repo.action.script.ScriptActionTrackingService">
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
<import resource="classpath:alfresco/calendar-services-context.xml"/>
|
<import resource="classpath:alfresco/calendar-services-context.xml"/>
|
||||||
<import resource="classpath:alfresco/comment-services-context.xml"/>
|
<import resource="classpath:alfresco/comment-services-context.xml"/>
|
||||||
<import resource="classpath:alfresco/discussions-services-context.xml"/>
|
<import resource="classpath:alfresco/discussions-services-context.xml"/>
|
||||||
|
<import resource="classpath:alfresco/download-services-context.xml"/>
|
||||||
<import resource="classpath:alfresco/links-services-context.xml"/>
|
<import resource="classpath:alfresco/links-services-context.xml"/>
|
||||||
<import resource="classpath:alfresco/quickshare-services-context.xml"/>
|
<import resource="classpath:alfresco/quickshare-services-context.xml"/>
|
||||||
<import resource="classpath:alfresco/rating-services-context.xml"/>
|
<import resource="classpath:alfresco/rating-services-context.xml"/>
|
||||||
|
31
config/alfresco/bootstrap/downloadsSpace.xml
Normal file
31
config/alfresco/bootstrap/downloadsSpace.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<view:view xmlns:view="http://www.alfresco.org/view/repository/1.0"
|
||||||
|
xmlns:d="http://www.alfresco.org/model/dictionary/1.0" xmlns:cm="http://www.alfresco.org/model/content/1.0"
|
||||||
|
xmlns:sys="http://www.alfresco.org/model/system/1.0" xmlns="">
|
||||||
|
|
||||||
|
<sys:container view:childName="${system.downloads_container.childname}">
|
||||||
|
<view:properties>
|
||||||
|
<sys:store-protocol>workspace</sys:store-protocol>
|
||||||
|
<sys:store-identifier>SpacesStore</sys:store-identifier>
|
||||||
|
<sys:node-uuid>downloads_container</sys:node-uuid>
|
||||||
|
<cm:name>${spaces.downloads.root.name}</cm:name>
|
||||||
|
<cm:title>${spaces.downloads.root.name}</cm:title>
|
||||||
|
<cm:description>${spaces.downloads.root.description}</cm:description>
|
||||||
|
</view:properties>
|
||||||
|
|
||||||
|
<!-- By default, anyone can add children (new downloads) -->
|
||||||
|
<!-- but not read, edit or delete other's sync sets -->
|
||||||
|
<view:acl view:inherit="false">
|
||||||
|
<view:ace view:access="ALLOWED">
|
||||||
|
<view:authority>GROUP_EVERYONE</view:authority>
|
||||||
|
<view:permission>AddChildren</view:permission>
|
||||||
|
</view:ace>
|
||||||
|
<view:ace view:access="ALLOWED">
|
||||||
|
<view:authority>ROLE_OWNER</view:authority>
|
||||||
|
<view:permission>FullControl</view:permission>
|
||||||
|
</view:ace>
|
||||||
|
</view:acl>
|
||||||
|
</sys:container>
|
||||||
|
|
||||||
|
</view:view>
|
||||||
|
|
141
config/alfresco/download-services-context.xml
Normal file
141
config/alfresco/download-services-context.xml
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
|
||||||
|
|
||||||
|
<beans>
|
||||||
|
|
||||||
|
<!-- Bootstrap the Sync model -->
|
||||||
|
<bean id="downloadDictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap">
|
||||||
|
<property name="models">
|
||||||
|
<list>
|
||||||
|
<value>alfresco/model/downloadModel.xml</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- Transaction Interceptors for the Calendar Service -->
|
||||||
|
<bean id="downloadServiceReadTxnAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
|
||||||
|
<property name="advice">
|
||||||
|
<ref bean="retryingReadTxnAdvice"/>
|
||||||
|
</property>
|
||||||
|
<property name="mappedNames">
|
||||||
|
<list>
|
||||||
|
<value>getDownloadStatus</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="downloadServiceWriteTxnAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
|
||||||
|
<property name="advice">
|
||||||
|
<ref bean="retryingWriteTxnAdvice"/>
|
||||||
|
</property>
|
||||||
|
<property name="mappedNames">
|
||||||
|
<list>
|
||||||
|
<value>cancelDownload</value>
|
||||||
|
<value>createDownload</value>
|
||||||
|
<value>deleteDownloads</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Download Service -->
|
||||||
|
<bean id="DownloadService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||||
|
<property name="proxyInterfaces">
|
||||||
|
<value>org.alfresco.service.cmr.download.DownloadService</value>
|
||||||
|
</property>
|
||||||
|
<property name="target">
|
||||||
|
<ref bean="downloadService" />
|
||||||
|
</property>
|
||||||
|
<property name="interceptorNames">
|
||||||
|
<list>
|
||||||
|
<idref bean="downloadServiceReadTxnAdvisor" />
|
||||||
|
<idref bean="downloadServiceWriteTxnAdvisor" />
|
||||||
|
<idref bean="checkTxnAdvisor" />
|
||||||
|
<idref bean="AuditMethodInterceptor" />
|
||||||
|
<idref bean="exceptionTranslator" />
|
||||||
|
<idref bean="DownloadService_security" />
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="downloadCannedQueryRegistry" class="org.alfresco.util.registry.NamedObjectRegistry">
|
||||||
|
<property name="storageType" value="org.alfresco.query.CannedQueryFactory"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean name="downloadGetDownloadsCannedQueryFactory" class="org.alfresco.repo.download.cannedquery.GetDownloadsCannedQueryFactory">
|
||||||
|
<property name="registry" ref="downloadCannedQueryRegistry"/>
|
||||||
|
<property name="nodeDAO" ref="nodeDAO"/>
|
||||||
|
<property name="qnameDAO" ref="qnameDAO"/>
|
||||||
|
<property name="cannedQueryDAO" ref="cannedQueryDAO"/>
|
||||||
|
<property name="tenantService" ref="tenantService"/>
|
||||||
|
<property name="methodSecurity" ref="DownloadService_security_deleteDownloads"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="downloadStorage" class="org.alfresco.repo.download.DownloadStorage">
|
||||||
|
<property name="importerBootstrap" ref="spacesBootstrap"/>
|
||||||
|
<property name="repositoryHelper" ref="repositoryHelper"/>
|
||||||
|
<property name="nodeService" ref="nodeService"/>
|
||||||
|
<property name="namespaceService" ref="namespaceService"/>
|
||||||
|
<property name="queryRegistry" ref="downloadCannedQueryRegistry"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
|
||||||
|
<bean id="downloadStatusUpdateService" class="org.alfresco.repo.download.DownloadStatusUpdateServiceImpl">
|
||||||
|
<property name="storage" ref="downloadStorage" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="downloadContentServiceHelper" class="org.alfresco.repo.download.LocalContentServiceHelper">
|
||||||
|
<property name="contentService" ref="contentService"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="createDownloadArchiveAction" class="org.alfresco.repo.download.CreateDownloadArchiveAction" parent="action-executer">
|
||||||
|
<property name="checkOutCheckInSerivce" ref="checkOutCheckInService"/>
|
||||||
|
<property name="contentServiceHelper" ref="downloadContentServiceHelper" />
|
||||||
|
<property name="downloadStorage" ref="downloadStorage" />
|
||||||
|
<property name="exporterService" ref="exporterComponent" />
|
||||||
|
<property name="maximumContentSize" value="${download.maxContentSize}" />
|
||||||
|
<property name="nodeService" ref="nodeService" />
|
||||||
|
<property name="publicAction" value="false"/>
|
||||||
|
<property name="transactionHelper" ref="retryingTransactionHelper"/>
|
||||||
|
<property name="updateService" ref="downloadStatusUpdateService"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="downloadActionServiceHelper" class="org.alfresco.repo.download.LocalActionServiceHelper">
|
||||||
|
<property name="localActionService" ref="actionService"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="downloadService" class="org.alfresco.repo.download.DownloadServiceImpl">
|
||||||
|
<property name="actionServiceHelper" ref="downloadActionServiceHelper"/>
|
||||||
|
<property name="downloadStorage" ref="downloadStorage"/>
|
||||||
|
<property name="transactionHelper" ref="retryingTransactionHelper"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="downloadCleanerJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
|
||||||
|
<property name="jobClass">
|
||||||
|
<value>org.alfresco.repo.download.DownloadsCleanupJob</value>
|
||||||
|
</property>
|
||||||
|
<property name="jobDataAsMap">
|
||||||
|
<map>
|
||||||
|
<entry key="downloadService" value-ref="DownloadService" />
|
||||||
|
<entry key="tenantAdminService" value-ref="tenantAdminService"/>
|
||||||
|
<entry key="maxAgeInMinutes" value="${download.cleaner.maxAgeMins}"/>
|
||||||
|
|
||||||
|
</map>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="downloadCleanerTrigger" class="org.alfresco.util.TriggerBean">
|
||||||
|
<property name="jobDetail">
|
||||||
|
<ref bean="downloadCleanerJobDetail" />
|
||||||
|
</property>
|
||||||
|
<property name="scheduler">
|
||||||
|
<ref bean="schedulerFactory" />
|
||||||
|
</property>
|
||||||
|
<property name="startDelayMinutes">
|
||||||
|
<value>${download.cleaner.startDelayMins}</value>
|
||||||
|
</property>
|
||||||
|
<property name="repeatIntervalMinutes">
|
||||||
|
<value>${download.cleaner.repeatIntervalMins}</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
</beans>
|
@@ -95,6 +95,9 @@ Inbound settings from iBatis
|
|||||||
<!-- Discussions CQ -->
|
<!-- Discussions CQ -->
|
||||||
<typeAlias alias="NodeWithChildrenEntity" type="org.alfresco.repo.discussion.cannedqueries.NodeWithChildrenEntity"/>
|
<typeAlias alias="NodeWithChildrenEntity" type="org.alfresco.repo.discussion.cannedqueries.NodeWithChildrenEntity"/>
|
||||||
|
|
||||||
|
<!-- Downloads CQ -->
|
||||||
|
<typeAlias alias="Download" type="org.alfresco.repo.download.cannedquery.DownloadEntity"/>
|
||||||
|
|
||||||
<!-- Patch -->
|
<!-- Patch -->
|
||||||
|
|
||||||
<typeAlias alias="Ids" type="org.alfresco.ibatis.IdsEntity"/>
|
<typeAlias alias="Ids" type="org.alfresco.ibatis.IdsEntity"/>
|
||||||
@@ -222,6 +225,7 @@ Inbound settings from iBatis
|
|||||||
<mapper resource="alfresco/ibatis/#resource.dialect#/query-blogs-common-SqlMap.xml"/>
|
<mapper resource="alfresco/ibatis/#resource.dialect#/query-blogs-common-SqlMap.xml"/>
|
||||||
<mapper resource="alfresco/ibatis/#resource.dialect#/query-calendar-common-SqlMap.xml"/>
|
<mapper resource="alfresco/ibatis/#resource.dialect#/query-calendar-common-SqlMap.xml"/>
|
||||||
<mapper resource="alfresco/ibatis/#resource.dialect#/query-copy-common-SqlMap.xml"/>
|
<mapper resource="alfresco/ibatis/#resource.dialect#/query-copy-common-SqlMap.xml"/>
|
||||||
|
<mapper resource="alfresco/ibatis/#resource.dialect#/query-downloads-common-SqlMap.xml"/>
|
||||||
<mapper resource="alfresco/ibatis/#resource.dialect#/query-discussion-common-SqlMap.xml"/>
|
<mapper resource="alfresco/ibatis/#resource.dialect#/query-discussion-common-SqlMap.xml"/>
|
||||||
</mappers>
|
</mappers>
|
||||||
|
|
||||||
|
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
|
||||||
|
<mapper namespace="alfresco.query.downloads">
|
||||||
|
|
||||||
|
<!-- -->
|
||||||
|
<!-- Result Maps -->
|
||||||
|
<!-- -->
|
||||||
|
|
||||||
|
<resultMap id="result_Download" type="Download">
|
||||||
|
<id property="id" column="id" jdbcType="BIGINT" javaType="java.lang.Long"/>
|
||||||
|
<association property="node" resultMap="alfresco.node.result_Node"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- GetDownloads Canned Query (model-specific) -->
|
||||||
|
|
||||||
|
<select id="select_GetDownloadsBeforeQuery" parameterType="org.alfresco.repo.download.cannedquery.GetDownloadsCannedQueryParams" resultMap="result_Download">
|
||||||
|
select
|
||||||
|
childNode.id as id,
|
||||||
|
childStore.protocol as protocol,
|
||||||
|
childStore.identifier as identifier,
|
||||||
|
childNode.uuid as uuid,
|
||||||
|
childNode.audit_created as audit_created,
|
||||||
|
childNode.audit_creator as audit_creator,
|
||||||
|
childNode.audit_accessed as audit_accessed,
|
||||||
|
childNode.audit_modified as audit_modified
|
||||||
|
from
|
||||||
|
alf_child_assoc assoc
|
||||||
|
join alf_node childNode on (childNode.id = assoc.child_node_id)
|
||||||
|
join alf_store childStore on (childStore.id = childNode.store_id)
|
||||||
|
where
|
||||||
|
assoc.parent_node_id = #{parentNodeId}
|
||||||
|
and childNode.type_qname_id = #{contentTypeQNameId}
|
||||||
|
order by
|
||||||
|
childNode.audit_modified ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
@@ -407,6 +407,7 @@
|
|||||||
<prop key="spaces.nodetemplates.childname">${spaces.nodetemplates.childname}</prop>
|
<prop key="spaces.nodetemplates.childname">${spaces.nodetemplates.childname}</prop>
|
||||||
<prop key="system.remote_credentials_container.childname">${system.remote_credentials_container.childname}</prop>
|
<prop key="system.remote_credentials_container.childname">${system.remote_credentials_container.childname}</prop>
|
||||||
<prop key="system.syncset_definition_container.childname">${system.syncset_definition_container.childname}</prop>
|
<prop key="system.syncset_definition_container.childname">${system.syncset_definition_container.childname}</prop>
|
||||||
|
<prop key="system.downloads_container.childname">${system.downloads_container.childname}</prop>
|
||||||
</props>
|
</props>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
@@ -699,6 +700,11 @@
|
|||||||
<prop key="messages">alfresco/messages/bootstrap-spaces</prop>
|
<prop key="messages">alfresco/messages/bootstrap-spaces</prop>
|
||||||
</props>
|
</props>
|
||||||
|
|
||||||
|
<props>
|
||||||
|
<prop key="path">/${system.system_container.childname}</prop>
|
||||||
|
<prop key="location">alfresco/bootstrap/downloadsSpace.xml</prop>
|
||||||
|
<prop key="messages">alfresco/messages/bootstrap-spaces</prop>
|
||||||
|
</props>
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
@@ -170,3 +170,6 @@ spaces.templates.email.workflowNotification.description=Workflow notification em
|
|||||||
|
|
||||||
spaces.nodeTemplatesSpace.name=Node Templates
|
spaces.nodeTemplatesSpace.name=Node Templates
|
||||||
spaces.nodeTemplatesSpace.description=Template Nodes for Share - Create New document
|
spaces.nodeTemplatesSpace.description=Template Nodes for Share - Create New document
|
||||||
|
|
||||||
|
spaces.downloads.root.name=Downloads
|
||||||
|
spaces.downloads.root.description=Root folder for downloads
|
||||||
|
1
config/alfresco/messages/download-model.properties
Normal file
1
config/alfresco/messages/download-model.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Download related messages
|
107
config/alfresco/model/downloadModel.xml
Normal file
107
config/alfresco/model/downloadModel.xml
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- xsi:schemaLocation="http://www.alfresco.org/model/dictionary/1.0 modelSchema.xsd" -->
|
||||||
|
<model name="download:downloadModel"
|
||||||
|
xmlns="http://www.alfresco.org/model/dictionary/1.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
|
||||||
|
<description>Alfresco Download Model</description>
|
||||||
|
<author>Alfresco</author>
|
||||||
|
<published>2012-07-31</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/content/1.0" prefix="cm"/>
|
||||||
|
</imports>
|
||||||
|
|
||||||
|
<namespaces>
|
||||||
|
<namespace uri="http://www.alfresco.org/model/download/1.0" prefix="download"/>
|
||||||
|
</namespaces>
|
||||||
|
|
||||||
|
<types>
|
||||||
|
<type name="download:download">
|
||||||
|
<parent>cm:content</parent>
|
||||||
|
<archive>false</archive>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<property name="download:recursive">
|
||||||
|
<type>d:boolean</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
<default>true</default>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="download:status">
|
||||||
|
<type>d:text</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
<default>PENDING</default>
|
||||||
|
<constraints>
|
||||||
|
<constraint type="LIST">
|
||||||
|
<parameter name="allowedValues">
|
||||||
|
<list>
|
||||||
|
<value>PENDING</value>
|
||||||
|
<value>IN_PROGRESS</value>
|
||||||
|
<value>DONE</value>
|
||||||
|
<value>MAX_CONTENT_SIZE_EXCEEDED</value>
|
||||||
|
<value>CANCELLED</value>
|
||||||
|
</list>
|
||||||
|
</parameter>
|
||||||
|
</constraint>
|
||||||
|
</constraints>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="download:sequenceNumber">
|
||||||
|
<type>d:int</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
<default>0</default>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="download:done">
|
||||||
|
<type>d:long</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
<default>0</default>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="download:total">
|
||||||
|
<type>d:long</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
<default>0</default>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="download:filesAdded">
|
||||||
|
<type>d:long</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
<default>0</default>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="download:totalFiles">
|
||||||
|
<type>d:long</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
<default>0</default>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="download:cancelled">
|
||||||
|
<type>d:boolean</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
<default>false</default>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
<associations>
|
||||||
|
<!-- This association points to each noderef which is a member of the sync set -->
|
||||||
|
<association name="download:requestedNodes">
|
||||||
|
<source>
|
||||||
|
<mandatory>false</mandatory>
|
||||||
|
<many>true</many>
|
||||||
|
</source>
|
||||||
|
<target>
|
||||||
|
<class>cm:cmobject</class>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
<many>true</many>
|
||||||
|
</target>
|
||||||
|
</association>
|
||||||
|
</associations>
|
||||||
|
<mandatory-aspects>
|
||||||
|
<aspect>cm:auditable</aspect>
|
||||||
|
</mandatory-aspects>
|
||||||
|
</type>
|
||||||
|
</types>
|
||||||
|
</model>
|
@@ -1019,6 +1019,31 @@
|
|||||||
<property name="methodName" value="listCalendarEntries" />
|
<property name="methodName" value="listCalendarEntries" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- ==================== -->
|
||||||
|
<!-- The Download Service -->
|
||||||
|
<!-- ==================== -->
|
||||||
|
|
||||||
|
<!-- The download service itself does not require any security restrictions, -->
|
||||||
|
<!-- they are imposed by the node and site services it uses to do its work. -->
|
||||||
|
<bean id="DownloadService_security" class="org.alfresco.repo.security.permissions.impl.AlwaysProceedMethodInterceptor" />
|
||||||
|
|
||||||
|
<!-- The canned queries that the calendar service uses do however need to check -->
|
||||||
|
<bean id="DownloadService_CannedQuery_security" class="org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor">
|
||||||
|
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||||
|
<property name="accessDecisionManager"><ref local="accessDecisionManager"/></property>
|
||||||
|
<property name="afterInvocationManager"><ref local="afterInvocationManager"/></property>
|
||||||
|
<property name="objectDefinitionSource">
|
||||||
|
<value>
|
||||||
|
org.alfresco.service.cmr.download.DownloadService.deleteDownloads=ACL_ALLOW
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="DownloadService_security_deleteDownloads" class="org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean">
|
||||||
|
<property name="methodSecurityInterceptor" ref="DownloadService_CannedQuery_security" />
|
||||||
|
<property name="service" value="org.alfresco.service.cmr.download.DownloadService" />
|
||||||
|
<property name="methodName" value="deleteDownloads" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- ==================== -->
|
<!-- ==================== -->
|
||||||
<!-- The Links Service -->
|
<!-- The Links Service -->
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
# Repository configuration
|
# Repository configuration
|
||||||
|
|
||||||
repository.name=Main Repository
|
repository.name=Main Repository
|
||||||
@@ -517,6 +516,9 @@ system.remote_credentials_container.childname=sys:remote_credentials
|
|||||||
# Folder for storing syncset definitions
|
# Folder for storing syncset definitions
|
||||||
system.syncset_definition_container.childname=sys:syncset_definitions
|
system.syncset_definition_container.childname=sys:syncset_definitions
|
||||||
|
|
||||||
|
# Folder for storing download archives
|
||||||
|
system.downloads_container.childname=sys:downloads
|
||||||
|
|
||||||
# Are user names case sensitive?
|
# Are user names case sensitive?
|
||||||
user.name.caseSensitive=false
|
user.name.caseSensitive=false
|
||||||
domain.name.caseSensitive=false
|
domain.name.caseSensitive=false
|
||||||
@@ -665,7 +667,6 @@ content.transformer.OpenOffice.mimeTypeLimits.ppam.pdf.maxSourceSizeKBytes=4096
|
|||||||
content.transformer.OpenOffice.mimeTypeLimits.sldx.pdf.maxSourceSizeKBytes=4096
|
content.transformer.OpenOffice.mimeTypeLimits.sldx.pdf.maxSourceSizeKBytes=4096
|
||||||
content.transformer.OpenOffice.mimeTypeLimits.sldm.pdf.maxSourceSizeKBytes=4096
|
content.transformer.OpenOffice.mimeTypeLimits.sldm.pdf.maxSourceSizeKBytes=4096
|
||||||
content.transformer.OpenOffice.mimeTypeLimits.vsd.pdf.maxSourceSizeKBytes=4096
|
content.transformer.OpenOffice.mimeTypeLimits.vsd.pdf.maxSourceSizeKBytes=4096
|
||||||
content.transformer.OpenOffice.mimeTypeLimits.xls.pdf.maxSourceSizeKBytes=10240
|
|
||||||
content.transformer.OpenOffice.mimeTypeLimits.xlsx.pdf.maxSourceSizeKBytes=1536
|
content.transformer.OpenOffice.mimeTypeLimits.xlsx.pdf.maxSourceSizeKBytes=1536
|
||||||
content.transformer.OpenOffice.mimeTypeLimits.xltx.pdf.maxSourceSizeKBytes=1536
|
content.transformer.OpenOffice.mimeTypeLimits.xltx.pdf.maxSourceSizeKBytes=1536
|
||||||
content.transformer.OpenOffice.mimeTypeLimits.xlsm.pdf.maxSourceSizeKBytes=1536
|
content.transformer.OpenOffice.mimeTypeLimits.xlsm.pdf.maxSourceSizeKBytes=1536
|
||||||
@@ -1018,5 +1019,17 @@ ticket.cleanup.cronExpression=0 0 * * * ?
|
|||||||
#
|
#
|
||||||
sample.site.disabled=false
|
sample.site.disabled=false
|
||||||
|
|
||||||
|
#
|
||||||
|
# Download Service Cleanup
|
||||||
|
#
|
||||||
|
download.cleaner.startDelayMins=60
|
||||||
|
download.cleaner.repeatIntervalMins=60
|
||||||
|
download.cleaner.maxAgeMins=60
|
||||||
|
|
||||||
# enable QuickShare - if false then the QuickShare-specific REST APIs will return 403 Forbidden
|
# enable QuickShare - if false then the QuickShare-specific REST APIs will return 403 Forbidden
|
||||||
system.quickshare.enabled=true
|
system.quickshare.enabled=true
|
||||||
|
|
||||||
|
#
|
||||||
|
# Download Service Limits, in bytes
|
||||||
|
#
|
||||||
|
download.maxContentSize=2152852358
|
||||||
|
@@ -108,6 +108,7 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
|
|||||||
private AuthenticationContext authenticationContext;
|
private AuthenticationContext authenticationContext;
|
||||||
private ActionTrackingService actionTrackingService;
|
private ActionTrackingService actionTrackingService;
|
||||||
private PolicyComponent policyComponent;
|
private PolicyComponent policyComponent;
|
||||||
|
private ActionServiceMonitor monitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The asynchronous action execution queues map of name, queue
|
* The asynchronous action execution queues map of name, queue
|
||||||
@@ -202,6 +203,14 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
|
|||||||
this.policyComponent = policyComponent;
|
this.policyComponent = policyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param monitor used to monitor running actions and execution times
|
||||||
|
*/
|
||||||
|
public void setMonitor(ActionServiceMonitor monitor)
|
||||||
|
{
|
||||||
|
this.monitor = monitor;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the asynchronous action execution queues
|
* Set the asynchronous action execution queues
|
||||||
*
|
*
|
||||||
@@ -706,8 +715,22 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
|
|||||||
actionTrackingService.recordActionExecuting(action);
|
actionTrackingService.recordActionExecuting(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the action
|
RunningAction runningAction = monitor.actionStarted(action);
|
||||||
directActionExecution(action, actionedUponNodeRef);
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Execute the action
|
||||||
|
directActionExecution(action, actionedUponNodeRef);
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
runningAction.setException(e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
monitor.actionCompleted(runningAction);
|
||||||
|
}
|
||||||
|
|
||||||
if (getTrackStatus(action))
|
if (getTrackStatus(action))
|
||||||
{
|
{
|
||||||
|
105
source/java/org/alfresco/repo/action/ActionServiceMonitor.java
Normal file
105
source/java/org/alfresco/repo/action/ActionServiceMonitor.java
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.action;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.action.Action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for monitoring running actions and accumulating statistics on actions that have been run.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class ActionServiceMonitor
|
||||||
|
{
|
||||||
|
private ConcurrentHashMap<UUID, RunningAction> runningActions = new ConcurrentHashMap<UUID, RunningAction>();
|
||||||
|
private ConcurrentHashMap<String, ActionStatistics> actionStatistics = new ConcurrentHashMap<String, ActionStatistics>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the {@link ActionServiceImpl} when an action is started.
|
||||||
|
*
|
||||||
|
* Adds the action to the list of currently running actions.
|
||||||
|
*
|
||||||
|
* @param action The action being started
|
||||||
|
* @return A {@link RunningAction} object used to track the status of the running action.
|
||||||
|
*/
|
||||||
|
public RunningAction actionStarted(Action action)
|
||||||
|
{
|
||||||
|
RunningAction runningAction = new RunningAction(action);
|
||||||
|
|
||||||
|
this.runningActions.put(runningAction.getId(), runningAction);
|
||||||
|
|
||||||
|
return runningAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the {@link ActionServiceImpl} when sn action completes.
|
||||||
|
*
|
||||||
|
* Removes the actions from the list of currently running actions, and updated the accumulated statistics for that action.
|
||||||
|
*
|
||||||
|
* @param action The {@link RunningAction} object returned by actionStatred.
|
||||||
|
*/
|
||||||
|
public void actionCompleted(RunningAction action)
|
||||||
|
{
|
||||||
|
runningActions.remove(action.getId());
|
||||||
|
updateActionStatisitcis(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateActionStatisitcis(RunningAction action)
|
||||||
|
{
|
||||||
|
String actionName = action.getActionName();
|
||||||
|
ActionStatistics actionStats = actionStatistics.get(actionName);
|
||||||
|
if (actionStats == null)
|
||||||
|
{
|
||||||
|
actionStatistics.putIfAbsent(actionName, new ActionStatistics(actionName));
|
||||||
|
actionStats = actionStatistics.get(actionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionStats.addAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The list of currently running actions.
|
||||||
|
*/
|
||||||
|
public List<RunningAction> getRunningActions()
|
||||||
|
{
|
||||||
|
return Collections.unmodifiableList(new ArrayList<RunningAction>(runningActions.values()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a count of the currently running actions
|
||||||
|
*/
|
||||||
|
public int getRunningActionCount()
|
||||||
|
{
|
||||||
|
return runningActions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a list of the accumulated action statistics.
|
||||||
|
*/
|
||||||
|
public List<ActionStatistics> getActionStatisitcs()
|
||||||
|
{
|
||||||
|
return Collections.unmodifiableList(new ArrayList<ActionStatistics>(actionStatistics.values()));
|
||||||
|
}
|
||||||
|
}
|
87
source/java/org/alfresco/repo/action/ActionStatistics.java
Normal file
87
source/java/org/alfresco/repo/action/ActionStatistics.java
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for accumulating and providing statistics on the invocations of a particualr action.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class ActionStatistics
|
||||||
|
{
|
||||||
|
private String actionName;
|
||||||
|
|
||||||
|
long invocationCount = 0;
|
||||||
|
long errorCount = 0;
|
||||||
|
long totalTime = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param actionName The name of the action this object will provide statistics for.
|
||||||
|
*/
|
||||||
|
public ActionStatistics(String actionName)
|
||||||
|
{
|
||||||
|
this.actionName = actionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accumulate statistics from action.
|
||||||
|
*/
|
||||||
|
public synchronized void addAction(RunningAction action)
|
||||||
|
{
|
||||||
|
invocationCount = invocationCount + 1;
|
||||||
|
if (action.hasError() == true)
|
||||||
|
{
|
||||||
|
errorCount = errorCount +1;
|
||||||
|
}
|
||||||
|
totalTime = totalTime + action.getElapsedTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The name of the actions this object has statistics for
|
||||||
|
*/
|
||||||
|
public String getActionName()
|
||||||
|
{
|
||||||
|
return actionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of times the action has been invoked
|
||||||
|
*/
|
||||||
|
public long getInvocationCount()
|
||||||
|
{
|
||||||
|
return invocationCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of time the invocation of this action has resulted in an exception
|
||||||
|
*/
|
||||||
|
public long getErrorCount()
|
||||||
|
{
|
||||||
|
return errorCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The average time for the invocation of this action
|
||||||
|
*/
|
||||||
|
public long getAverageTime()
|
||||||
|
{
|
||||||
|
return totalTime / invocationCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
85
source/java/org/alfresco/repo/action/RunningAction.java
Normal file
85
source/java/org/alfresco/repo/action/RunningAction.java
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package org.alfresco.repo.action;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.action.Action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for tracking the invocation of an action.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class RunningAction
|
||||||
|
{
|
||||||
|
private UUID id = UUID.randomUUID();
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private Thread thread;
|
||||||
|
|
||||||
|
private Date started;
|
||||||
|
|
||||||
|
private boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param action The action being run
|
||||||
|
*/
|
||||||
|
public RunningAction(Action action)
|
||||||
|
{
|
||||||
|
this.name = action.getActionDefinitionName();
|
||||||
|
this.started = new Date();
|
||||||
|
this.thread = Thread.currentThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The name of the action this object is tracking
|
||||||
|
*/
|
||||||
|
public String getActionName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The name of thread the action is being run on
|
||||||
|
*/
|
||||||
|
public String getThread()
|
||||||
|
{
|
||||||
|
return thread.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The generated id for the action invocation
|
||||||
|
*/
|
||||||
|
public UUID getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The time since the action was started
|
||||||
|
*/
|
||||||
|
public long getElapsedTime()
|
||||||
|
{
|
||||||
|
return System.currentTimeMillis() - started.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the {@link ActionServiceImpl} if the action generates an exception during invocation.
|
||||||
|
*/
|
||||||
|
public void setException(Throwable e)
|
||||||
|
{
|
||||||
|
this.exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true, if setException was called
|
||||||
|
*/
|
||||||
|
public boolean hasError()
|
||||||
|
{
|
||||||
|
return exceptionThrown;
|
||||||
|
}
|
||||||
|
}
|
167
source/java/org/alfresco/repo/download/AbstractExporter.java
Normal file
167
source/java/org/alfresco/repo/download/AbstractExporter.java
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.security.AccessPermission;
|
||||||
|
import org.alfresco.service.cmr.view.Exporter;
|
||||||
|
import org.alfresco.service.cmr.view.ExporterContext;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
|
||||||
|
public class AbstractExporter implements Exporter
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(ExporterContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startNamespace(String prefix, String uri)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endNamespace(String prefix)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startNode(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endNode(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startReference(NodeRef nodeRef, QName childName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endReference(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startAspects(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startAspect(NodeRef nodeRef, QName aspect)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endAspect(NodeRef nodeRef, QName aspect)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endAspects(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startACL(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void permission(NodeRef nodeRef, AccessPermission permission)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endACL(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startProperties(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startProperty(NodeRef nodeRef, QName property)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endProperty(NodeRef nodeRef, QName property)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endProperties(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startValueCollection(NodeRef nodeRef, QName property)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startValueMLText(NodeRef nodeRef, Locale locale)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endValueMLText(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void value(NodeRef nodeRef, QName property, Object value, int index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void content(NodeRef nodeRef, QName property, InputStream content,
|
||||||
|
ContentData contentData, int index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endValueCollection(NodeRef nodeRef, QName property)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startAssocs(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startAssoc(NodeRef nodeRef, QName assoc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endAssoc(NodeRef nodeRef, QName assoc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endAssocs(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warning(String warning)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void end()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActionServiceHelper interface.
|
||||||
|
*
|
||||||
|
* Allows the download service to switch between executing the zip creation process in the current alfresco node,
|
||||||
|
* or on a remote node.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public interface ActionServiceHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations should trigger the CreateDownloadArchiveAction on the provided downloadNode
|
||||||
|
*
|
||||||
|
* @param downloadNode
|
||||||
|
*/
|
||||||
|
void executeAction(NodeRef downloadNode);
|
||||||
|
|
||||||
|
}
|
228
source/java/org/alfresco/repo/download/BaseExporter.java
Normal file
228
source/java/org/alfresco/repo/download/BaseExporter.java
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.security.AccessPermission;
|
||||||
|
import org.alfresco.service.cmr.view.Exporter;
|
||||||
|
import org.alfresco.service.cmr.view.ExporterContext;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base {@link Exporter} providing a default implementation of all methods.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
abstract class BaseExporter implements Exporter
|
||||||
|
{
|
||||||
|
private CheckOutCheckInService checkOutCheckInService;
|
||||||
|
protected NodeService nodeService;
|
||||||
|
|
||||||
|
BaseExporter(CheckOutCheckInService checkOutCheckInService, NodeService nodeService)
|
||||||
|
{
|
||||||
|
this.checkOutCheckInService = checkOutCheckInService;
|
||||||
|
this.nodeService = nodeService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(ExporterContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startNamespace(String prefix, String uri)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endNamespace(String prefix)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startNode(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endNode(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startReference(NodeRef nodeRef, QName childName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endReference(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startAspects(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startAspect(NodeRef nodeRef, QName aspect)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endAspect(NodeRef nodeRef, QName aspect)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endAspects(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startACL(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void permission(NodeRef nodeRef, AccessPermission permission)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endACL(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startProperties(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startProperty(NodeRef nodeRef, QName property)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endProperty(NodeRef nodeRef, QName property)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endProperties(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startValueCollection(NodeRef nodeRef, QName property)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startValueMLText(NodeRef nodeRef, Locale locale)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endValueMLText(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void value(NodeRef nodeRef, QName property, Object value, int index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void content(NodeRef nodeRef, QName property, InputStream content, ContentData contentData, int index)
|
||||||
|
{
|
||||||
|
if (checkOutCheckInService.isCheckedOut(nodeRef) == true)
|
||||||
|
{
|
||||||
|
String owner = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_OWNER);
|
||||||
|
if (AuthenticationUtil.getRunAsUser().equals(owner) == true)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkOutCheckInService.isWorkingCopy(nodeRef) == true)
|
||||||
|
{
|
||||||
|
String owner = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_WORKING_COPY_OWNER);
|
||||||
|
if (AuthenticationUtil.getRunAsUser().equals(owner) == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentImpl(nodeRef, property, content, contentData, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for actually dealing with the content.
|
||||||
|
*
|
||||||
|
* Called by the content method, after filtering for working copies.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected abstract void contentImpl(NodeRef nodeRef, QName property, InputStream content, ContentData contentData, int index);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endValueCollection(NodeRef nodeRef, QName property)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startAssocs(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startAssoc(NodeRef nodeRef, QName assoc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endAssoc(NodeRef nodeRef, QName assoc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endAssocs(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warning(String warning)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void end()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ContentServiceHelper interface.
|
||||||
|
*
|
||||||
|
* Allows us to switch between the zip creation process updating content using a local content service
|
||||||
|
* and updating the content through a remote alfresco node.
|
||||||
|
*
|
||||||
|
* @author amiller
|
||||||
|
*/
|
||||||
|
public interface ContentServiceHelper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Implementations should update the content of downlaodNode with contents of archiveFile.
|
||||||
|
*
|
||||||
|
* @param downloadNode
|
||||||
|
* @param archiveFile
|
||||||
|
* @throws ContentIOException
|
||||||
|
* @throws FileNotFoundException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void updateContent(NodeRef downloadNode, File archiveFile) throws ContentIOException, FileNotFoundException, IOException;
|
||||||
|
}
|
@@ -0,0 +1,311 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.model.RenditionModel;
|
||||||
|
import org.alfresco.repo.action.executer.ActionExecuter;
|
||||||
|
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
|
import org.alfresco.service.cmr.action.Action;
|
||||||
|
import org.alfresco.service.cmr.action.ParameterDefinition;
|
||||||
|
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadRequest;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadStatus;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadStatus.Status;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.view.ExporterCrawlerParameters;
|
||||||
|
import org.alfresco.service.cmr.view.ExporterService;
|
||||||
|
import org.alfresco.service.cmr.view.Location;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.TempFileProvider;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ActionExecuter} for creating an archive (ie. zip) file containing
|
||||||
|
* content from the repository.
|
||||||
|
*
|
||||||
|
* The maximum total size of the content which can be downloaded is controlled
|
||||||
|
* by the maximumContentSie property. -1 indicates no limit.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class CreateDownloadArchiveAction extends ActionExecuterAbstractBase
|
||||||
|
{
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CreateDownloadArchiveAction.class);
|
||||||
|
|
||||||
|
|
||||||
|
private static final String CREATION_ERROR = "Unexpected error creating archive file for download";
|
||||||
|
private static final String TEMP_FILE_PREFIX = "download";
|
||||||
|
private static final String TEMP_FILE_SUFFIX = ".zip";
|
||||||
|
|
||||||
|
// Dependencies
|
||||||
|
private CheckOutCheckInService checkOutCheckInService;
|
||||||
|
private ContentServiceHelper contentServiceHelper;
|
||||||
|
private DownloadStorage downloadStorage;
|
||||||
|
private ExporterService exporterService;
|
||||||
|
private NodeService nodeService;
|
||||||
|
private RetryingTransactionHelper transactionHelper;
|
||||||
|
private DownloadStatusUpdateService updateService;
|
||||||
|
|
||||||
|
private long maximumContentSize = -1l;
|
||||||
|
|
||||||
|
private static class SizeEstimator extends BaseExporter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param checkOutCheckInService
|
||||||
|
* @param nodeService
|
||||||
|
*/
|
||||||
|
SizeEstimator(CheckOutCheckInService checkOutCheckInService, NodeService nodeService)
|
||||||
|
{
|
||||||
|
super(checkOutCheckInService, nodeService);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long size = 0;
|
||||||
|
private long fileCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void contentImpl(NodeRef nodeRef, QName property, InputStream content, ContentData contentData, int index)
|
||||||
|
{
|
||||||
|
size = size + contentData.getSize();
|
||||||
|
fileCount = fileCount + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize()
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFileCount()
|
||||||
|
{
|
||||||
|
return fileCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dependency setters
|
||||||
|
public void setCheckOutCheckInSerivce(CheckOutCheckInService checkOutCheckInService)
|
||||||
|
{
|
||||||
|
this.checkOutCheckInService = checkOutCheckInService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setContentServiceHelper(ContentServiceHelper contentServiceHelper)
|
||||||
|
{
|
||||||
|
this.contentServiceHelper = contentServiceHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDownloadStorage(DownloadStorage downloadStorage)
|
||||||
|
{
|
||||||
|
this.downloadStorage = downloadStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExporterService(ExporterService exporterService)
|
||||||
|
{
|
||||||
|
this.exporterService = exporterService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum total size of content that can be added to a single
|
||||||
|
* download. -1 indicates no limit.
|
||||||
|
*/
|
||||||
|
public void setMaximumContentSize(long maximumContentSize)
|
||||||
|
{
|
||||||
|
this.maximumContentSize = maximumContentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeService(NodeService nodeService)
|
||||||
|
{
|
||||||
|
this.nodeService = nodeService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTransactionHelper(RetryingTransactionHelper transactionHelper)
|
||||||
|
{
|
||||||
|
this.transactionHelper = transactionHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdateService(DownloadStatusUpdateService updateService)
|
||||||
|
{
|
||||||
|
this.updateService = updateService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an archive file containing content from the repository.
|
||||||
|
*
|
||||||
|
* Uses the {@link ExporterService} with custom exporters to create the
|
||||||
|
* archive files.
|
||||||
|
*
|
||||||
|
* @param actionedUponNodeRef Download node containing information required
|
||||||
|
* to create the archive file, and which will eventually have its content
|
||||||
|
* updated with the archive file.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void executeImpl(Action action, final NodeRef actionedUponNodeRef)
|
||||||
|
{
|
||||||
|
// Get the download request data and set up the exporter crawler parameters.
|
||||||
|
final DownloadRequest downloadRequest = downloadStorage.getDownloadRequest(actionedUponNodeRef);
|
||||||
|
|
||||||
|
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object doWork() throws Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
ExporterCrawlerParameters crawlerParameters = new ExporterCrawlerParameters();
|
||||||
|
|
||||||
|
Location exportFrom = new Location(downloadRequest.getRequetedNodeRefs());
|
||||||
|
crawlerParameters.setExportFrom(exportFrom);
|
||||||
|
|
||||||
|
crawlerParameters.setCrawlSelf(true);
|
||||||
|
crawlerParameters.setExcludeChildAssocs(new QName[] {RenditionModel.ASSOC_RENDITION});
|
||||||
|
crawlerParameters.setExcludeAspects(new QName[] {ContentModel.ASPECT_WORKING_COPY});
|
||||||
|
|
||||||
|
// Get an estimate of the size for statuses
|
||||||
|
SizeEstimator estimator = new SizeEstimator(checkOutCheckInService, nodeService);
|
||||||
|
exporterService.exportView(estimator, crawlerParameters, null);
|
||||||
|
|
||||||
|
if (maximumContentSize > 0 && estimator.getSize() > maximumContentSize)
|
||||||
|
{
|
||||||
|
maximumContentSizeExceeded(actionedUponNodeRef, estimator.getSize(), estimator.getFileCount());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
createDownload(actionedUponNodeRef, crawlerParameters, estimator);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}, downloadRequest.getOwner());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void maximumContentSizeExceeded(final NodeRef actionedUponNodeRef, final long size, final long fileCount)
|
||||||
|
{
|
||||||
|
log.debug("Maximum contentent size ({}), exceeded ({})", maximumContentSize, size);
|
||||||
|
|
||||||
|
//Update the content and set the status to done.
|
||||||
|
transactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object execute() throws Throwable
|
||||||
|
{
|
||||||
|
DownloadStatus status = new DownloadStatus(Status.MAX_CONTENT_SIZE_EXCEEDED, maximumContentSize, size, 0, fileCount);
|
||||||
|
updateService.update(actionedUponNodeRef, status, 1);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createDownload(final NodeRef actionedUponNodeRef, ExporterCrawlerParameters crawlerParameters, SizeEstimator estimator)
|
||||||
|
{
|
||||||
|
// perform the actual export
|
||||||
|
final File tempFile = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX);
|
||||||
|
final ZipDownloadExporter handler = new ZipDownloadExporter(tempFile, checkOutCheckInService, nodeService, transactionHelper, updateService, downloadStorage, actionedUponNodeRef, estimator.getSize(), estimator.getFileCount());
|
||||||
|
|
||||||
|
try {
|
||||||
|
exporterService.exportView(handler, crawlerParameters, null);
|
||||||
|
archiveCreationComplete(actionedUponNodeRef, tempFile, handler);
|
||||||
|
}
|
||||||
|
catch (DownloadCancelledException ex)
|
||||||
|
{
|
||||||
|
downloadCancelled(actionedUponNodeRef, handler);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tempFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void archiveCreationComplete(final NodeRef actionedUponNodeRef, final File tempFile,
|
||||||
|
final ZipDownloadExporter handler)
|
||||||
|
{
|
||||||
|
//Update the content and set the status to done.
|
||||||
|
transactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object execute() throws Throwable
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
contentServiceHelper.updateContent(actionedUponNodeRef, tempFile);
|
||||||
|
DownloadStatus status = new DownloadStatus(Status.DONE, handler.getDone(), handler.getTotal(), handler.getFilesAdded(), handler.getTotalFiles());
|
||||||
|
updateService.update(actionedUponNodeRef, status, handler.getNextSequenceNumber());
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (ContentIOException ex)
|
||||||
|
{
|
||||||
|
throw new DownloadServiceException(CREATION_ERROR, ex);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException ex)
|
||||||
|
{
|
||||||
|
throw new DownloadServiceException(CREATION_ERROR, ex);
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
throw new DownloadServiceException(CREATION_ERROR, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void downloadCancelled(final NodeRef actionedUponNodeRef, final ZipDownloadExporter handler)
|
||||||
|
{
|
||||||
|
//Update the content and set the status to done.
|
||||||
|
transactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Object execute() throws Throwable
|
||||||
|
{
|
||||||
|
DownloadStatus status = new DownloadStatus(Status.CANCELLED, handler.getDone(), handler.getTotal(), handler.getFilesAdded(), handler.getTotalFiles());
|
||||||
|
updateService.update(actionedUponNodeRef, status, handler.getNextSequenceNumber());
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,18 @@
|
|||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.view.ExporterException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown by ZipDownloadExporter, if a download is cancelled mid flow.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class DownloadCancelledException extends ExporterException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 4694929866014032096L;
|
||||||
|
|
||||||
|
public DownloadCancelledException()
|
||||||
|
{
|
||||||
|
super("Download Cancelled");
|
||||||
|
}
|
||||||
|
}
|
48
source/java/org/alfresco/repo/download/DownloadModel.java
Normal file
48
source/java/org/alfresco/repo/download/DownloadModel.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility interface for the downloadModel.xml
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public interface DownloadModel
|
||||||
|
{
|
||||||
|
/** Download Model URI */
|
||||||
|
static final String DOWNLOAD_MODEL_1_0_URI = "http://www.alfresco.org/model/download/1.0";
|
||||||
|
|
||||||
|
/** Type QName */
|
||||||
|
static final QName TYPE_DOWNLOAD = QName.createQName(DOWNLOAD_MODEL_1_0_URI, "download");
|
||||||
|
|
||||||
|
// Property QNames
|
||||||
|
static final QName PROP_CANCELLED = QName.createQName(DOWNLOAD_MODEL_1_0_URI, "cancelled");
|
||||||
|
static final QName PROP_DONE = QName.createQName(DOWNLOAD_MODEL_1_0_URI, "done");
|
||||||
|
static final QName PROP_FILES_ADDED = QName.createQName(DOWNLOAD_MODEL_1_0_URI, "filesAdded");
|
||||||
|
static final QName PROP_RECURSIVE = QName.createQName(DOWNLOAD_MODEL_1_0_URI, "recursive");
|
||||||
|
static final QName PROP_SEQUENCE_NUMBER = QName.createQName(DOWNLOAD_MODEL_1_0_URI, "sequenceNumber");
|
||||||
|
static final QName PROP_STATUS = QName.createQName(DOWNLOAD_MODEL_1_0_URI, "status");
|
||||||
|
static final QName PROP_TOTAL = QName.createQName(DOWNLOAD_MODEL_1_0_URI, "total");
|
||||||
|
static final QName PROP_TOTAL_FILES = QName.createQName(DOWNLOAD_MODEL_1_0_URI, "totalFiles");
|
||||||
|
|
||||||
|
// Associations
|
||||||
|
static final QName ASSOC_REQUESTED_NODES = QName.createQName(DOWNLOAD_MODEL_1_0_URI, "requestedNodes");
|
||||||
|
}
|
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download Service Exception class
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class DownloadServiceException extends AlfrescoRuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1826926526215676002L;
|
||||||
|
|
||||||
|
public DownloadServiceException(String message, Throwable cause)
|
||||||
|
{
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
134
source/java/org/alfresco/repo/download/DownloadServiceImpl.java
Normal file
134
source/java/org/alfresco/repo/download/DownloadServiceImpl.java
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.repo.download.cannedquery.DownloadEntity;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadService;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadStatus;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.util.ParameterCheck;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the download service.
|
||||||
|
*
|
||||||
|
* Persists the download reqest and then uses a local action service to execute
|
||||||
|
* the {@link CreateDownloadArchiveAction}.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class DownloadServiceImpl implements DownloadService {
|
||||||
|
|
||||||
|
// Dependencies
|
||||||
|
private ActionServiceHelper actionServiceHelper;
|
||||||
|
private DownloadStorage downloadStorage;
|
||||||
|
private RetryingTransactionHelper transactionHelper;
|
||||||
|
|
||||||
|
// Dependency setters
|
||||||
|
public void setActionServiceHelper(ActionServiceHelper actionServiceHelper)
|
||||||
|
{
|
||||||
|
this.actionServiceHelper = actionServiceHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTransactionHelper(RetryingTransactionHelper transactionHelper)
|
||||||
|
{
|
||||||
|
this.transactionHelper = transactionHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDownloadStorage(DownloadStorage downloadStorage)
|
||||||
|
{
|
||||||
|
this.downloadStorage = downloadStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NodeRef createDownload(final NodeRef[] requestedNodes, final boolean recursive) {
|
||||||
|
ParameterCheck.mandatory("nodeRefs", requestedNodes);
|
||||||
|
if (requestedNodes.length < 1)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Need at least 1 node ref");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is done in a new transaction to avoid node not found errors when the zip creation occurs
|
||||||
|
// on a remote transformation server.
|
||||||
|
NodeRef downloadNode = transactionHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NodeRef execute() throws Throwable
|
||||||
|
{
|
||||||
|
//Create a download node
|
||||||
|
NodeRef downloadNode = downloadStorage.createDownloadNode(recursive);
|
||||||
|
|
||||||
|
//Add requested nodes
|
||||||
|
for (NodeRef node : requestedNodes)
|
||||||
|
{
|
||||||
|
downloadStorage.addNodeToDownload(downloadNode, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return downloadNode;
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
|
||||||
|
//Trigger the action.
|
||||||
|
actionServiceHelper.executeAction(downloadNode);
|
||||||
|
|
||||||
|
return downloadNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DownloadStatus getDownloadStatus(NodeRef downloadNode) {
|
||||||
|
ParameterCheck.mandatory("downloadNode", downloadNode);
|
||||||
|
|
||||||
|
return downloadStorage.getDownloadStatus(downloadNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see org.alfresco.service.cmr.download.DownloadService#deleteDownloads(java.util.Date)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void deleteDownloads(Date before)
|
||||||
|
{
|
||||||
|
List<List<DownloadEntity>> downloadPages = downloadStorage.getDownloadsCreatedBefore(before);
|
||||||
|
for (List<DownloadEntity> page : downloadPages)
|
||||||
|
{
|
||||||
|
for (DownloadEntity download : page)
|
||||||
|
{
|
||||||
|
downloadStorage.delete(download.getNodeRef());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see org.alfresco.service.cmr.download.DownloadService#cancelDownload(org.alfresco.service.cmr.repository.NodeRef)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cancelDownload(NodeRef downloadNodeRef)
|
||||||
|
{
|
||||||
|
ParameterCheck.mandatory("downloadNodeRef", downloadNodeRef);
|
||||||
|
|
||||||
|
downloadStorage.cancelDownload(downloadNodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,408 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import net.sf.acegisecurity.Authentication;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.model.Repository;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
|
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadService;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadStatus;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadStatus.Status;
|
||||||
|
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.security.PermissionService;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.test.junitrules.AlfrescoPerson;
|
||||||
|
import org.alfresco.util.test.junitrules.ApplicationContextInit;
|
||||||
|
import org.alfresco.util.test.junitrules.TemporaryNodes;
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.RuleChain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test for DownloadServiceImpl
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class DownloadServiceIntegrationTest
|
||||||
|
{
|
||||||
|
public static final long MAX_TIME = 5000;
|
||||||
|
|
||||||
|
private static final long PAUSE_TIME = 1000;
|
||||||
|
|
||||||
|
// Rule to initialize the default Alfresco spring configuration
|
||||||
|
public static ApplicationContextInit APP_CONTEXT_INIT = new ApplicationContextInit();
|
||||||
|
|
||||||
|
// Rules to create 2 test users.
|
||||||
|
public static AlfrescoPerson TEST_USER = new AlfrescoPerson(APP_CONTEXT_INIT, "User");
|
||||||
|
public static AlfrescoPerson TEST_USER2 = new AlfrescoPerson(APP_CONTEXT_INIT, "User 2");
|
||||||
|
|
||||||
|
// A rule to manage test nodes reused across all the test methods
|
||||||
|
public static TemporaryNodes STATIC_TEST_NODES = new TemporaryNodes(APP_CONTEXT_INIT);
|
||||||
|
|
||||||
|
// Tie them together in a static Rule Chain
|
||||||
|
@ClassRule public static RuleChain ruleChain = RuleChain.outerRule(APP_CONTEXT_INIT)
|
||||||
|
.around(TEST_USER)
|
||||||
|
.around(STATIC_TEST_NODES);
|
||||||
|
|
||||||
|
// A rule to manage test nodes use in each test method
|
||||||
|
@Rule public TemporaryNodes testNodes = new TemporaryNodes(APP_CONTEXT_INIT);
|
||||||
|
|
||||||
|
// Service under test
|
||||||
|
public static DownloadService DOWNLOAD_SERVICE;
|
||||||
|
|
||||||
|
// Various supporting services
|
||||||
|
private static CheckOutCheckInService CHECK_OUT_CHECK_IN_SERVICE;
|
||||||
|
private static ContentService CONTENT_SERVICE;
|
||||||
|
private static NodeService NODE_SERVICE;
|
||||||
|
private static PermissionService PERMISSION_SERVICE;
|
||||||
|
private static RetryingTransactionHelper TRANSACTION_HELPER;
|
||||||
|
|
||||||
|
// Test Content
|
||||||
|
private NodeRef rootFolder;
|
||||||
|
private NodeRef rootFile;
|
||||||
|
|
||||||
|
private NodeRef level1Folder1;
|
||||||
|
|
||||||
|
private NodeRef level1Folder2;
|
||||||
|
|
||||||
|
private Set<String> allEntries;
|
||||||
|
|
||||||
|
private NodeRef fileToCheckout;
|
||||||
|
|
||||||
|
@BeforeClass public static void init()
|
||||||
|
{
|
||||||
|
// Resolve required services
|
||||||
|
CHECK_OUT_CHECK_IN_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("CheckOutCheckInService", CheckOutCheckInService.class);
|
||||||
|
CONTENT_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("contentService", ContentService.class);
|
||||||
|
DOWNLOAD_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("DownloadService", DownloadService.class);
|
||||||
|
NODE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("NodeService", NodeService.class);
|
||||||
|
PERMISSION_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("PermissionService", PermissionService.class);
|
||||||
|
TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the test content
|
||||||
|
*/
|
||||||
|
@Before public void createContent()
|
||||||
|
{
|
||||||
|
allEntries = new TreeSet<String>();
|
||||||
|
|
||||||
|
AuthenticationUtil.setRunAsUserSystem();
|
||||||
|
|
||||||
|
Repository repositoryHelper = (Repository) APP_CONTEXT_INIT.getApplicationContext().getBean("repositoryHelper");
|
||||||
|
NodeRef COMPANY_HOME = repositoryHelper.getCompanyHome();
|
||||||
|
|
||||||
|
// Create some static test content
|
||||||
|
rootFolder = testNodes.createNode(COMPANY_HOME, "rootFolder", ContentModel.TYPE_FOLDER, AuthenticationUtil.getAdminUserName());
|
||||||
|
allEntries.add("rootFolder/");
|
||||||
|
|
||||||
|
rootFile = testNodes.createNodeWithTextContent(COMPANY_HOME, "rootFile.txt", ContentModel.TYPE_CONTENT, AuthenticationUtil.getAdminUserName(), "Root file content");
|
||||||
|
allEntries.add("rootFile.txt");
|
||||||
|
|
||||||
|
testNodes.createNodeWithTextContent(rootFolder, "level1File.txt", ContentModel.TYPE_CONTENT, AuthenticationUtil.getAdminUserName(), "Level 1 file content");
|
||||||
|
allEntries.add("rootFolder/level1File.txt");
|
||||||
|
|
||||||
|
level1Folder1 = testNodes.createNode(rootFolder, "level1Folder1", ContentModel.TYPE_FOLDER, AuthenticationUtil.getAdminUserName());
|
||||||
|
allEntries.add("rootFolder/level1Folder1/");
|
||||||
|
|
||||||
|
level1Folder2 = testNodes.createNode(rootFolder, "level1Folder2", ContentModel.TYPE_FOLDER, AuthenticationUtil.getAdminUserName());
|
||||||
|
allEntries.add("rootFolder/level1Folder2/");
|
||||||
|
|
||||||
|
testNodes.createNode(rootFolder, "level1EmptyFolder", ContentModel.TYPE_FOLDER, AuthenticationUtil.getAdminUserName());
|
||||||
|
allEntries.add("rootFolder/level1EmptyFolder/");
|
||||||
|
|
||||||
|
testNodes.createNodeWithTextContent(level1Folder1, "level2File.txt", ContentModel.TYPE_CONTENT, AuthenticationUtil.getAdminUserName(), "Level 2 file content");
|
||||||
|
allEntries.add("rootFolder/level1Folder1/level2File.txt");
|
||||||
|
|
||||||
|
testNodes.createNodeWithTextContent(level1Folder2, "level2File.txt", ContentModel.TYPE_CONTENT, AuthenticationUtil.getAdminUserName(), "Level 2 file content");
|
||||||
|
allEntries.add("rootFolder/level1Folder2/level2File.txt");
|
||||||
|
|
||||||
|
fileToCheckout = testNodes.createNodeWithTextContent(level1Folder2, "fileToCheckout.txt", ContentModel.TYPE_CONTENT, AuthenticationUtil.getAdminUserName(), "Level 2 file content");
|
||||||
|
// Add the lock and version aspects to the created node
|
||||||
|
NODE_SERVICE.addAspect(fileToCheckout, ContentModel.ASPECT_VERSIONABLE, null);
|
||||||
|
NODE_SERVICE.addAspect(fileToCheckout, ContentModel.ASPECT_LOCKABLE, null);
|
||||||
|
|
||||||
|
allEntries.add("rootFolder/level1Folder2/fileToCheckout.txt");
|
||||||
|
PERMISSION_SERVICE.setPermission(level1Folder2, TEST_USER.getUsername(), PermissionService.ALL_PERMISSIONS, true);
|
||||||
|
PERMISSION_SERVICE.setPermission(fileToCheckout, TEST_USER.getUsername(), PermissionService.ALL_PERMISSIONS, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void createDownload() throws IOException, InterruptedException
|
||||||
|
{
|
||||||
|
// Initiate the download
|
||||||
|
final NodeRef downloadNode = DOWNLOAD_SERVICE.createDownload(new NodeRef[] {rootFile, rootFolder}, true);
|
||||||
|
Assert.assertNotNull(downloadNode);
|
||||||
|
|
||||||
|
testNodes.addNodeRef(downloadNode);
|
||||||
|
|
||||||
|
// Validate that the download node has been persisted correctly.
|
||||||
|
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object execute() throws Throwable
|
||||||
|
{
|
||||||
|
Map<QName, Serializable> properties = NODE_SERVICE.getProperties(downloadNode);
|
||||||
|
Assert.assertEquals(Boolean.TRUE, properties.get(DownloadModel.PROP_RECURSIVE));
|
||||||
|
|
||||||
|
List<AssociationRef> associations = NODE_SERVICE.getTargetAssocs(downloadNode, DownloadModel.ASSOC_REQUESTED_NODES);
|
||||||
|
for (AssociationRef association : associations)
|
||||||
|
{
|
||||||
|
Assert.assertTrue(association.getTargetRef().equals(rootFile) || association.getTargetRef().equals(rootFolder));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
DownloadStatus status = getDownloadStatus(downloadNode);
|
||||||
|
while (status.getStatus() == Status.PENDING)
|
||||||
|
{
|
||||||
|
Thread.sleep(PAUSE_TIME);
|
||||||
|
status = getDownloadStatus(downloadNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertEquals(5l, status.getTotalFiles());
|
||||||
|
|
||||||
|
long elapsedTime = waitForDownload(downloadNode);
|
||||||
|
|
||||||
|
Assert.assertTrue("Maximum creation time exceeded!", elapsedTime < MAX_TIME);
|
||||||
|
|
||||||
|
|
||||||
|
// Validate the content.
|
||||||
|
final Set<String> entryNames = getEntries(downloadNode);
|
||||||
|
|
||||||
|
validateEntries(entryNames, allEntries, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateEntries(final Set<String> entryNames, final Set<String> expectedEntries, boolean onlyExpected)
|
||||||
|
{
|
||||||
|
Set<String> copy = new TreeSet<String>(entryNames);
|
||||||
|
for (String expectedEntry : expectedEntries)
|
||||||
|
{
|
||||||
|
Assert.assertTrue("Missing entry:- " + expectedEntry, copy.contains(expectedEntry));
|
||||||
|
copy.remove(expectedEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onlyExpected == true)
|
||||||
|
{
|
||||||
|
Assert.assertTrue("Unexpected entries", copy.isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> getEntries(final NodeRef downloadNode)
|
||||||
|
{
|
||||||
|
return TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Set<String>>()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> execute() throws Throwable
|
||||||
|
{
|
||||||
|
Set<String> entryNames = new TreeSet<String>();
|
||||||
|
ContentReader reader = CONTENT_SERVICE.getReader(downloadNode, ContentModel.PROP_CONTENT);
|
||||||
|
ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream(reader.getContentInputStream());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ZipArchiveEntry zipEntry = zipInputStream.getNextZipEntry();
|
||||||
|
while (zipEntry != null)
|
||||||
|
{
|
||||||
|
String name = zipEntry.getName();
|
||||||
|
entryNames.add(name);
|
||||||
|
zipEntry = zipInputStream.getNextZipEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
zipInputStream.close();
|
||||||
|
}
|
||||||
|
return entryNames;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private long waitForDownload(final NodeRef downloadNode) throws InterruptedException
|
||||||
|
{
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
// Wait for the staus to become done.
|
||||||
|
DownloadStatus status;
|
||||||
|
long elapsedTime;
|
||||||
|
do {
|
||||||
|
status = getDownloadStatus(downloadNode);
|
||||||
|
elapsedTime = System.currentTimeMillis() - startTime;
|
||||||
|
if (status.isComplete() == false)
|
||||||
|
{
|
||||||
|
Thread.sleep(PAUSE_TIME);
|
||||||
|
}
|
||||||
|
} while (status.isComplete() == false && elapsedTime < MAX_TIME);
|
||||||
|
return elapsedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private DownloadStatus getDownloadStatus(final NodeRef downloadNode)
|
||||||
|
{
|
||||||
|
return TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<DownloadStatus>()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DownloadStatus execute() throws Throwable
|
||||||
|
{
|
||||||
|
return DOWNLOAD_SERVICE.getDownloadStatus(downloadNode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void deleteBefore() throws InterruptedException
|
||||||
|
{
|
||||||
|
NodeRef beforeNodeRef;
|
||||||
|
NodeRef afterNodeRef;
|
||||||
|
Date beforeTime;
|
||||||
|
|
||||||
|
beforeNodeRef = DOWNLOAD_SERVICE.createDownload(new NodeRef[] {level1Folder1}, true);
|
||||||
|
testNodes.addNodeRef(beforeNodeRef);
|
||||||
|
waitForDownload(beforeNodeRef);
|
||||||
|
|
||||||
|
beforeTime = new Date();
|
||||||
|
|
||||||
|
afterNodeRef = DOWNLOAD_SERVICE.createDownload(new NodeRef[] {level1Folder2}, true);
|
||||||
|
testNodes.addNodeRef(afterNodeRef);
|
||||||
|
waitForDownload(afterNodeRef);
|
||||||
|
|
||||||
|
DOWNLOAD_SERVICE.deleteDownloads(beforeTime);
|
||||||
|
|
||||||
|
Assert.assertFalse(NODE_SERVICE.exists(beforeNodeRef));
|
||||||
|
Assert.assertTrue(NODE_SERVICE.exists(afterNodeRef));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void cancel() throws InterruptedException
|
||||||
|
{
|
||||||
|
// Initiate the download
|
||||||
|
final NodeRef downloadNode = DOWNLOAD_SERVICE.createDownload(new NodeRef[] {rootFile, rootFolder}, true);
|
||||||
|
Assert.assertNotNull(downloadNode);
|
||||||
|
|
||||||
|
testNodes.addNodeRef(downloadNode);
|
||||||
|
|
||||||
|
DOWNLOAD_SERVICE.cancelDownload(downloadNode);
|
||||||
|
|
||||||
|
DownloadStatus status = getDownloadStatus(downloadNode);
|
||||||
|
int retryCount = 0;
|
||||||
|
while (status.getStatus() != Status.CANCELLED && retryCount < 5)
|
||||||
|
{
|
||||||
|
retryCount++;
|
||||||
|
Thread.sleep(PAUSE_TIME);
|
||||||
|
status = getDownloadStatus(downloadNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertEquals(Status.CANCELLED, status.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test verifies that a user is given the correct file, when it is checked. The user who checked out
|
||||||
|
* the file should get the working copy, while any other user should get the default version.
|
||||||
|
* @throws InterruptedException
|
||||||
|
*/
|
||||||
|
@Test public void workingCopies() throws InterruptedException
|
||||||
|
{
|
||||||
|
final Set<String> preCheckoutExpectedEntries = new TreeSet<String>();
|
||||||
|
preCheckoutExpectedEntries.add("level1Folder2/");
|
||||||
|
preCheckoutExpectedEntries.add("level1Folder2/level2File.txt");
|
||||||
|
preCheckoutExpectedEntries.add("level1Folder2/fileToCheckout.txt");
|
||||||
|
|
||||||
|
validateWorkingCopyFolder(preCheckoutExpectedEntries, level1Folder2, TEST_USER.getUsername());
|
||||||
|
validateWorkingCopyFolder(preCheckoutExpectedEntries, level1Folder2, TEST_USER2.getUsername());
|
||||||
|
|
||||||
|
Authentication previousAuth = AuthenticationUtil.getFullAuthentication();
|
||||||
|
AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER.getUsername());
|
||||||
|
NodeRef workingCopy;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
workingCopy = CHECK_OUT_CHECK_IN_SERVICE.checkout(fileToCheckout);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
AuthenticationUtil.setFullAuthentication(previousAuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
validateWorkingCopyFolder(preCheckoutExpectedEntries, level1Folder2, TEST_USER2.getUsername());
|
||||||
|
|
||||||
|
final Set<String> postCheckoutExpectedEntries = new TreeSet<String>();
|
||||||
|
postCheckoutExpectedEntries.add("level1Folder2/");
|
||||||
|
postCheckoutExpectedEntries.add("level1Folder2/level2File.txt");
|
||||||
|
postCheckoutExpectedEntries.add("level1Folder2/fileToCheckout (Working Copy).txt");
|
||||||
|
validateWorkingCopyFolder(postCheckoutExpectedEntries, level1Folder2, TEST_USER.getUsername());
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
previousAuth = AuthenticationUtil.getFullAuthentication();
|
||||||
|
AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER.getUsername());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CHECK_OUT_CHECK_IN_SERVICE.checkin(workingCopy, null);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
AuthenticationUtil.setFullAuthentication(previousAuth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validateWorkingCopyFolder(preCheckoutExpectedEntries, level1Folder2, TEST_USER.getUsername());
|
||||||
|
validateWorkingCopyFolder(preCheckoutExpectedEntries, level1Folder2, TEST_USER2.getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateWorkingCopyFolder(final Set<String> expectedEntries, final NodeRef folder, final String userID) throws InterruptedException
|
||||||
|
{
|
||||||
|
Authentication previousAuthentication = AuthenticationUtil.getFullAuthentication();
|
||||||
|
AuthenticationUtil.setFullyAuthenticatedUser(userID);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
final NodeRef downloadNode = DOWNLOAD_SERVICE.createDownload(new NodeRef[] {folder}, true);
|
||||||
|
waitForDownload(downloadNode);
|
||||||
|
|
||||||
|
validateEntries(getEntries(downloadNode), expectedEntries, true);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
AuthenticationUtil.setFullAuthentication(previousAuthentication);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.download.DownloadStatus;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for updating the status of a download.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public interface DownloadStatusUpdateService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Update and persist the status of the download.
|
||||||
|
*
|
||||||
|
* Implementations should only do this if sequenceNumber is greater than
|
||||||
|
* the sequenceNumber of the previous update, to prevent out of order
|
||||||
|
* updates.
|
||||||
|
*
|
||||||
|
* @param nodeRef The download node, whose status is to be updated.
|
||||||
|
* @param status The new status
|
||||||
|
* @param sequenceNumber
|
||||||
|
*/
|
||||||
|
void update(NodeRef nodeRef, DownloadStatus status, int sequenceNumber);
|
||||||
|
}
|
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.download.DownloadStatus;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.mockito.internal.progress.SequenceNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation class responsible for update the status of a download node.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class DownloadStatusUpdateServiceImpl implements DownloadStatusUpdateService
|
||||||
|
{
|
||||||
|
|
||||||
|
// Dependencies
|
||||||
|
private DownloadStorage storage;
|
||||||
|
|
||||||
|
// Dependency setters
|
||||||
|
public void setStorage(DownloadStorage storage)
|
||||||
|
{
|
||||||
|
this.storage = storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(NodeRef nodeRef, DownloadStatus status, int sequenceNumber)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Update the status of the download node, if and only if sequenceNumber is
|
||||||
|
// greater than the sequence number of the last update.
|
||||||
|
int currentSequenceNumber = storage.getSequenceNumber(nodeRef);
|
||||||
|
|
||||||
|
if (currentSequenceNumber < sequenceNumber)
|
||||||
|
{
|
||||||
|
storage.updateStatus(nodeRef, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
255
source/java/org/alfresco/repo/download/DownloadStorage.java
Normal file
255
source/java/org/alfresco/repo/download/DownloadStorage.java
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2005-2012 Alfresco Software, Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* License rights for this program may be obtained from Alfresco Software, Ltd.
|
||||||
|
* pursuant to a written agreement and any use of this program without such an
|
||||||
|
* agreement is prohibited.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.query.CannedQueryFactory;
|
||||||
|
import org.alfresco.query.CannedQueryResults;
|
||||||
|
import org.alfresco.repo.download.cannedquery.DownloadEntity;
|
||||||
|
import org.alfresco.repo.download.cannedquery.GetDownloadsCannedQuery;
|
||||||
|
import org.alfresco.repo.download.cannedquery.GetDownloadsCannedQueryFactory;
|
||||||
|
import org.alfresco.repo.importer.ImporterBootstrap;
|
||||||
|
import org.alfresco.repo.model.Repository;
|
||||||
|
import org.alfresco.repo.node.SystemNodeUtils;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadRequest;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadStatus;
|
||||||
|
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.NamespaceService;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.registry.NamedObjectRegistry;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for the persistence of {@link DownloadDefinition} objects using lower-level
|
||||||
|
* repo services such as the {@link NodeService}. The higher-level business logic around these CRUD calls
|
||||||
|
* is contained within the {@link DownloadServiceImpl}.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class DownloadStorage
|
||||||
|
{
|
||||||
|
private static final Log log = LogFactory.getLog(DownloadStorage.class);
|
||||||
|
|
||||||
|
// service dependencies
|
||||||
|
private ImporterBootstrap bootstrap;
|
||||||
|
private Repository repositoryHelper;
|
||||||
|
private NodeService nodeService;
|
||||||
|
private NamespaceService namespaceService;
|
||||||
|
private NamedObjectRegistry<CannedQueryFactory<? extends Object>> queryRegistry;
|
||||||
|
|
||||||
|
public void setImporterBootstrap(ImporterBootstrap bootstrap)
|
||||||
|
{
|
||||||
|
this.bootstrap = bootstrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQueryRegistry(NamedObjectRegistry<CannedQueryFactory<? extends Object>> queryRegistry)
|
||||||
|
{
|
||||||
|
this.queryRegistry = queryRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRepositoryHelper(Repository repositoryHelper)
|
||||||
|
{
|
||||||
|
this.repositoryHelper = repositoryHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeService(NodeService nodeService)
|
||||||
|
{
|
||||||
|
this.nodeService = nodeService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNamespaceService(NamespaceService namespaceService)
|
||||||
|
{
|
||||||
|
this.namespaceService = namespaceService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method finds the SyncSet Definition Container NodeRef, creating one if it does not exist.
|
||||||
|
*
|
||||||
|
* @return the syncset definition container
|
||||||
|
*/
|
||||||
|
public NodeRef getOrCreateDowloadContainer()
|
||||||
|
{
|
||||||
|
NodeRef downloadsContainer = getContainer();
|
||||||
|
|
||||||
|
if (downloadsContainer == null)
|
||||||
|
{
|
||||||
|
if (log.isInfoEnabled())
|
||||||
|
log.info("Lazy creating the Downloads System Container ");
|
||||||
|
|
||||||
|
downloadsContainer = SystemNodeUtils.getOrCreateSystemChildContainer(getContainerQName(), nodeService, repositoryHelper).getFirst();
|
||||||
|
}
|
||||||
|
return downloadsContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NodeRef getContainer()
|
||||||
|
{
|
||||||
|
return SystemNodeUtils.getSystemChildContainer(getContainerQName(), nodeService, repositoryHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private QName getContainerQName()
|
||||||
|
{
|
||||||
|
String name = bootstrap.getConfiguration().getProperty("system.downloads_container.childname");
|
||||||
|
QName container = QName.createQName(name, namespaceService);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public NodeRef createDownloadNode(boolean recursive)
|
||||||
|
{
|
||||||
|
NodeRef downloadsContainer = getOrCreateDowloadContainer();
|
||||||
|
|
||||||
|
Map<QName, Serializable> downloadProperties = new HashMap<QName, Serializable>();
|
||||||
|
downloadProperties.put(DownloadModel.PROP_RECURSIVE, recursive);
|
||||||
|
|
||||||
|
ChildAssociationRef newChildAssoc = nodeService.createNode(downloadsContainer,
|
||||||
|
ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN,
|
||||||
|
DownloadModel.TYPE_DOWNLOAD,
|
||||||
|
downloadProperties);
|
||||||
|
|
||||||
|
final NodeRef downloadNodeRef = newChildAssoc.getChildRef();
|
||||||
|
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
msg.append("Created Download. ")
|
||||||
|
.append("', Download-NodeRef=");
|
||||||
|
log.debug(msg.toString());
|
||||||
|
}
|
||||||
|
return downloadNodeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancelDownload(NodeRef downloadNodeRef)
|
||||||
|
{
|
||||||
|
validateNode(downloadNodeRef);
|
||||||
|
|
||||||
|
nodeService.setProperty(downloadNodeRef, DownloadModel.PROP_CANCELLED, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCancelled(NodeRef downloadNodeRef)
|
||||||
|
{
|
||||||
|
validateNode(downloadNodeRef);
|
||||||
|
|
||||||
|
return (Boolean)nodeService.getProperty(downloadNodeRef, DownloadModel.PROP_CANCELLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNodeToDownload(NodeRef downloadNode, NodeRef nodeToAdd)
|
||||||
|
{
|
||||||
|
nodeService.createAssociation(downloadNode, nodeToAdd, DownloadModel.ASSOC_REQUESTED_NODES);
|
||||||
|
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
msg.append("Node added to Download-NodeRef '")
|
||||||
|
.append(downloadNode).append("'. RequestedNode=")
|
||||||
|
.append(nodeToAdd);
|
||||||
|
log.debug(msg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownloadRequest getDownloadRequest(NodeRef downloadNodeRef)
|
||||||
|
{
|
||||||
|
validateNode(downloadNodeRef);
|
||||||
|
Map<QName, Serializable> properties = nodeService.getProperties(downloadNodeRef);
|
||||||
|
|
||||||
|
List<AssociationRef> requestedNodes = nodeService.getTargetAssocs(downloadNodeRef, DownloadModel.ASSOC_REQUESTED_NODES);
|
||||||
|
|
||||||
|
return new DownloadRequest((Boolean)properties.get(DownloadModel.PROP_RECURSIVE), requestedNodes, (String)properties.get(ContentModel.PROP_CREATOR));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateNode(NodeRef downloadNodeRef)
|
||||||
|
{
|
||||||
|
if (nodeService.getType(downloadNodeRef).equals(DownloadModel.TYPE_DOWNLOAD) == false)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Invlaid node type for nodeRef:-" + downloadNodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownloadStatus getDownloadStatus(NodeRef downloadNodeRef)
|
||||||
|
{
|
||||||
|
validateNode(downloadNodeRef);
|
||||||
|
Map<QName, Serializable> properties = nodeService.getProperties(downloadNodeRef);
|
||||||
|
|
||||||
|
Long done = (Long)properties.get(DownloadModel.PROP_DONE);
|
||||||
|
Long total = (Long)properties.get(DownloadModel.PROP_TOTAL);
|
||||||
|
Long filesAdded = (Long)properties.get(DownloadModel.PROP_FILES_ADDED);
|
||||||
|
Long totalFiles = (Long)properties.get(DownloadModel.PROP_TOTAL_FILES);
|
||||||
|
|
||||||
|
return new DownloadStatus(DownloadStatus.Status.valueOf((String)properties.get(DownloadModel.PROP_STATUS)),
|
||||||
|
done != null ? done.longValue() : 0l,
|
||||||
|
total != null ? total.longValue() : 0l,
|
||||||
|
filesAdded != null ? filesAdded.longValue() : 0l,
|
||||||
|
totalFiles != null ? totalFiles.longValue() : 0l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSequenceNumber(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
validateNode(nodeRef);
|
||||||
|
Serializable sequenceNumber = nodeService.getProperty(nodeRef, DownloadModel.PROP_SEQUENCE_NUMBER);
|
||||||
|
|
||||||
|
return ((Integer)sequenceNumber).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateStatus(NodeRef nodeRef, DownloadStatus status)
|
||||||
|
{
|
||||||
|
validateNode(nodeRef);
|
||||||
|
|
||||||
|
nodeService.setProperty(nodeRef, DownloadModel.PROP_STATUS, status.getStatus().toString());
|
||||||
|
nodeService.setProperty(nodeRef, DownloadModel.PROP_DONE, new Long(status.getDone()));
|
||||||
|
nodeService.setProperty(nodeRef, DownloadModel.PROP_TOTAL, new Long(status.getTotal()));
|
||||||
|
nodeService.setProperty(nodeRef, DownloadModel.PROP_FILES_ADDED, status.getFilesAdded());
|
||||||
|
nodeService.setProperty(nodeRef, DownloadModel.PROP_TOTAL_FILES, status.getTotalFiles());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the downloads created before before.
|
||||||
|
*/
|
||||||
|
public List<List<DownloadEntity>> getDownloadsCreatedBefore(Date before)
|
||||||
|
{
|
||||||
|
NodeRef container = getContainer();
|
||||||
|
|
||||||
|
if (container == null)
|
||||||
|
{
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the factory
|
||||||
|
GetDownloadsCannedQueryFactory getDownloadCannedQueryFactory =
|
||||||
|
(GetDownloadsCannedQueryFactory)queryRegistry.getNamedObject("downloadGetDownloadsCannedQueryFactory");
|
||||||
|
|
||||||
|
// Run the canned query
|
||||||
|
GetDownloadsCannedQuery cq = (GetDownloadsCannedQuery)getDownloadCannedQueryFactory.getDownloadsCannedQuery(container, before);
|
||||||
|
|
||||||
|
// Execute the canned query
|
||||||
|
CannedQueryResults<DownloadEntity> results = cq.execute();
|
||||||
|
|
||||||
|
return results.getPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the download node identified by nodeRef
|
||||||
|
* @param nodeRef
|
||||||
|
*/
|
||||||
|
public void delete(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
validateNode(nodeRef);
|
||||||
|
|
||||||
|
nodeService.deleteNode(nodeRef);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||||
|
import org.alfresco.repo.tenant.Tenant;
|
||||||
|
import org.alfresco.repo.tenant.TenantAdminService;
|
||||||
|
import org.alfresco.repo.tenant.TenantUtil;
|
||||||
|
import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadService;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.quartz.Job;
|
||||||
|
import org.quartz.JobDataMap;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the clean up of download nodes.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class DownloadsCleanupJob implements Job
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final String KEY_DOWNLOAD_SERVICE = "downloadService";
|
||||||
|
private static final String KEY_TENANT_ADMIN_SERVICE = "tenantAdminService";
|
||||||
|
private static final String KEY_MAX_AGE = "maxAgeInMinutes";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void execute(JobExecutionContext context) throws JobExecutionException
|
||||||
|
{
|
||||||
|
JobDataMap jobData = context.getJobDetail().getJobDataMap();
|
||||||
|
|
||||||
|
// extract the services and max age to use
|
||||||
|
final DownloadService downloadService = (DownloadService)jobData.get(KEY_DOWNLOAD_SERVICE);
|
||||||
|
final TenantAdminService tenantAdminService = (TenantAdminService)jobData.get(KEY_TENANT_ADMIN_SERVICE);
|
||||||
|
final int maxAgeInMinutes = Integer.parseInt((String)jobData.get(KEY_MAX_AGE));
|
||||||
|
|
||||||
|
final DateTime before = new DateTime().minusMinutes(maxAgeInMinutes);
|
||||||
|
|
||||||
|
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||||
|
{
|
||||||
|
public Object doWork() throws Exception
|
||||||
|
{
|
||||||
|
downloadService.deleteDownloads(before.toDate());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, AuthenticationUtil.getSystemUserName());
|
||||||
|
|
||||||
|
if ((tenantAdminService != null) && tenantAdminService.isEnabled())
|
||||||
|
{
|
||||||
|
List<Tenant> tenants = tenantAdminService.getAllTenants();
|
||||||
|
for (Tenant tenant : tenants)
|
||||||
|
{
|
||||||
|
TenantUtil.runAsSystemTenant(new TenantRunAsWork<Object>()
|
||||||
|
{
|
||||||
|
public Object doWork() throws Exception
|
||||||
|
{
|
||||||
|
downloadService.deleteDownloads(before.toDate());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, tenant.getTenantDomain());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.action.Action;
|
||||||
|
import org.alfresco.service.cmr.action.ActionService;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.util.ParameterCheck;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link ActionServiceHelper} which schedules the zip creation process to run in the same alfresco node
|
||||||
|
* as the caller.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class LocalActionServiceHelper implements InitializingBean, ActionServiceHelper
|
||||||
|
{
|
||||||
|
private ActionService localActionService;
|
||||||
|
|
||||||
|
public void setLocalActionService(ActionService localActionService)
|
||||||
|
{
|
||||||
|
this.localActionService = localActionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executeAction(NodeRef downloadNode)
|
||||||
|
{
|
||||||
|
Action action = localActionService.createAction("createDownloadArchiveAction");
|
||||||
|
action.setExecuteAsynchronously(true);
|
||||||
|
|
||||||
|
localActionService.executeAction(action, downloadNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception
|
||||||
|
{
|
||||||
|
ParameterCheck.mandatory("localActionServer", localActionService);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ContentServiceHelper} implementation which uses the local ContentService to update the content.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class LocalContentServiceHelper implements ContentServiceHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
private ContentService contentService;
|
||||||
|
|
||||||
|
public void setContentService(ContentService contentService)
|
||||||
|
{
|
||||||
|
this.contentService = contentService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateContent(final NodeRef downloadNode, final File archiveFile) throws ContentIOException, FileNotFoundException, IOException
|
||||||
|
{
|
||||||
|
//RunAsSystem to mimic clustered behavior, and bypass quotas when using S3 storage.
|
||||||
|
AuthenticationUtil.runAsSystem(new RunAsWork<Object>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Object doWork() throws Exception
|
||||||
|
{
|
||||||
|
ContentWriter writer = contentService.getWriter(downloadNode, ContentModel.PROP_CONTENT, true);
|
||||||
|
FileCopyUtils.copy(new FileInputStream(archiveFile), writer.getContentOutputStream());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
300
source/java/org/alfresco/repo/download/ZipDownloadExporter.java
Normal file
300
source/java/org/alfresco/repo/download/ZipDownloadExporter.java
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
|
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadStatus;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadStatus.Status;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.view.ExporterContext;
|
||||||
|
import org.alfresco.service.cmr.view.ExporterException;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.Pair;
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.UnicodeExtraFieldPolicy;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for exporting node content to a ZIP file
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class ZipDownloadExporter extends BaseExporter
|
||||||
|
{
|
||||||
|
private static Logger log = LoggerFactory.getLogger(ZipDownloadExporter.class);
|
||||||
|
|
||||||
|
private static final String PATH_SEPARATOR = "/";
|
||||||
|
|
||||||
|
protected ZipArchiveOutputStream zipStream;
|
||||||
|
|
||||||
|
private NodeRef downloadNodeRef;
|
||||||
|
private int sequenceNumber = 1;
|
||||||
|
private long total;
|
||||||
|
private long done;
|
||||||
|
private long totalFileCount;
|
||||||
|
private long filesAddedCount;
|
||||||
|
|
||||||
|
private RetryingTransactionHelper transactionHelper;
|
||||||
|
private DownloadStorage downloadStorage;
|
||||||
|
private DownloadStatusUpdateService updateService;
|
||||||
|
|
||||||
|
private Deque<Pair<String, NodeRef>> path = new LinkedList<Pair<String, NodeRef>>();
|
||||||
|
private String currentName;
|
||||||
|
|
||||||
|
private OutputStream outputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct
|
||||||
|
*
|
||||||
|
* @param destDir
|
||||||
|
* @param zipFile
|
||||||
|
* @param transactionHelper
|
||||||
|
* @param l
|
||||||
|
* @param actionedUponNodeRef
|
||||||
|
* @param dataFile
|
||||||
|
* @param contentDir
|
||||||
|
*/
|
||||||
|
public ZipDownloadExporter(File zipFile, CheckOutCheckInService checkOutCheckInService, NodeService nodeService, RetryingTransactionHelper transactionHelper, DownloadStatusUpdateService updateService, DownloadStorage downloadStorage, NodeRef downloadNodeRef, long total, long totalFileCount)
|
||||||
|
{
|
||||||
|
super(checkOutCheckInService, nodeService);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.outputStream = new FileOutputStream(zipFile);
|
||||||
|
this.updateService = updateService;
|
||||||
|
this.transactionHelper = transactionHelper;
|
||||||
|
this.downloadStorage = downloadStorage;
|
||||||
|
|
||||||
|
this.downloadNodeRef = downloadNodeRef;
|
||||||
|
this.total = total;
|
||||||
|
this.totalFileCount = totalFileCount;
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e)
|
||||||
|
{
|
||||||
|
throw new ExporterException("Failed to create zip file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(final ExporterContext context)
|
||||||
|
{
|
||||||
|
zipStream = new ZipArchiveOutputStream(outputStream);
|
||||||
|
// NOTE: This encoding allows us to workaround bug...
|
||||||
|
// http://bugs.sun.com/bugdatabase/view_bug.do;:WuuT?bug_id=4820807
|
||||||
|
zipStream.setEncoding("UTF-8");
|
||||||
|
zipStream.setCreateUnicodeExtraFields(UnicodeExtraFieldPolicy.ALWAYS);
|
||||||
|
zipStream.setUseLanguageEncodingFlag(true);
|
||||||
|
zipStream.setFallbackToUTF8(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startNode(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
this.currentName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
|
||||||
|
path.push(new Pair<String, NodeRef>(currentName, nodeRef));
|
||||||
|
if (ContentModel.TYPE_FOLDER.equals(nodeService.getType(nodeRef)))
|
||||||
|
{
|
||||||
|
String path = getPath() + PATH_SEPARATOR;
|
||||||
|
ZipArchiveEntry archiveEntry = new ZipArchiveEntry(path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
zipStream.putArchiveEntry(archiveEntry);
|
||||||
|
zipStream.closeArchiveEntry();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new ExporterException("Unexpected IOException adding folder entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contentImpl(NodeRef nodeRef, QName property, InputStream content, ContentData contentData, int index)
|
||||||
|
{
|
||||||
|
// if the content stream to output is empty, then just return content descriptor as is
|
||||||
|
if (content == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// ALF-2016
|
||||||
|
ZipArchiveEntry zipEntry=new ZipArchiveEntry(getPath());
|
||||||
|
zipStream.putArchiveEntry(zipEntry);
|
||||||
|
|
||||||
|
// copy export stream to zip
|
||||||
|
copyStream(zipStream, content);
|
||||||
|
|
||||||
|
zipStream.closeArchiveEntry();
|
||||||
|
filesAddedCount = filesAddedCount + 1;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new ExporterException("Failed to zip export stream", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endNode(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void end()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
zipStream.close();
|
||||||
|
}
|
||||||
|
catch (IOException error)
|
||||||
|
{
|
||||||
|
throw new ExporterException("Unexpected error closing zip stream!", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPath()
|
||||||
|
{
|
||||||
|
if (path.size() < 1)
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("No elements in path!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<Pair<String, NodeRef>> iter = path.descendingIterator();
|
||||||
|
StringBuilder pathBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
while (iter.hasNext())
|
||||||
|
{
|
||||||
|
Pair<String, NodeRef> element = iter.next();
|
||||||
|
|
||||||
|
pathBuilder.append(element.getFirst());
|
||||||
|
if (iter.hasNext())
|
||||||
|
{
|
||||||
|
pathBuilder.append(PATH_SEPARATOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy input stream to output stream
|
||||||
|
*
|
||||||
|
* @param output output stream
|
||||||
|
* @param in input stream
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void copyStream(OutputStream output, InputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[2048 * 10];
|
||||||
|
int read = in.read(buffer, 0, 2048 *10);
|
||||||
|
while (read != -1)
|
||||||
|
{
|
||||||
|
output.write(buffer, 0, read);
|
||||||
|
done = done + read;
|
||||||
|
updateStatus();
|
||||||
|
checkCancelled();
|
||||||
|
read = in.read(buffer, 0, 2048 *10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkCancelled()
|
||||||
|
{
|
||||||
|
boolean downloadCancelled = transactionHelper.doInTransaction(new RetryingTransactionCallback<Boolean>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Boolean execute() throws Throwable
|
||||||
|
{
|
||||||
|
return downloadStorage.isCancelled(downloadNodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, true, true);
|
||||||
|
|
||||||
|
if ( downloadCancelled == true)
|
||||||
|
{
|
||||||
|
log.debug("Download cancelled");
|
||||||
|
throw new DownloadCancelledException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStatus()
|
||||||
|
{
|
||||||
|
transactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object execute() throws Throwable
|
||||||
|
{
|
||||||
|
DownloadStatus status = new DownloadStatus(Status.IN_PROGRESS, done, total, filesAddedCount, totalFileCount);
|
||||||
|
|
||||||
|
updateService.update(downloadNodeRef, status, getNextSequenceNumber());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNextSequenceNumber()
|
||||||
|
{
|
||||||
|
return sequenceNumber++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getDone()
|
||||||
|
{
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotal()
|
||||||
|
{
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFilesAdded()
|
||||||
|
{
|
||||||
|
return filesAddedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotalFiles()
|
||||||
|
{
|
||||||
|
return totalFileCount;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download.cannedquery;
|
||||||
|
|
||||||
|
import org.alfresco.repo.query.NodeBackedEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download Entity - used by GetDownloads CQ
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class DownloadEntity extends NodeBackedEntity
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Default constructor
|
||||||
|
*/
|
||||||
|
public DownloadEntity()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownloadEntity(Long parentNodeId, Long nameQNameId, Long contentTypeQNameId)
|
||||||
|
{
|
||||||
|
super(parentNodeId, nameQNameId, contentTypeQNameId);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download.cannedquery;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.query.CannedQueryParameters;
|
||||||
|
import org.alfresco.repo.domain.query.CannedQueryDAO;
|
||||||
|
import org.alfresco.repo.security.permissions.impl.acegi.AbstractCannedQueryPermissions;
|
||||||
|
import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadService;
|
||||||
|
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides the GetDownloads canned queries} used by the
|
||||||
|
* {@link DownloadService}.deleteDOwnloads.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class GetDownloadsCannedQuery extends AbstractCannedQueryPermissions<DownloadEntity>
|
||||||
|
{
|
||||||
|
private static final String QUERY_NAMESPACE = "alfresco.query.downloads";
|
||||||
|
private static final String QUERY_SELECT_GET_DOWNLOADS = "select_GetDownloadsBeforeQuery";
|
||||||
|
|
||||||
|
private final CannedQueryDAO cannedQueryDAO;
|
||||||
|
|
||||||
|
public GetDownloadsCannedQuery(
|
||||||
|
CannedQueryDAO cannedQueryDAO,
|
||||||
|
MethodSecurityBean<DownloadEntity> methodSecurity,
|
||||||
|
CannedQueryParameters params)
|
||||||
|
{
|
||||||
|
super(params, methodSecurity);
|
||||||
|
this.cannedQueryDAO = cannedQueryDAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<DownloadEntity> queryAndFilter(CannedQueryParameters parameters)
|
||||||
|
{
|
||||||
|
Object paramBeanObj = parameters.getParameterBean();
|
||||||
|
if (paramBeanObj == null)
|
||||||
|
{
|
||||||
|
throw new NullPointerException("Null GetDownloadss query params");
|
||||||
|
}
|
||||||
|
|
||||||
|
GetDownloadsCannedQueryParams paramsBean = (GetDownloadsCannedQueryParams)paramBeanObj;
|
||||||
|
|
||||||
|
// note: refer to SQL for specific DB filtering (eg.parent node and optionally blog integration aspect, etc)
|
||||||
|
List<DownloadEntity> results = cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_DOWNLOADS, paramBeanObj, 0, Integer.MAX_VALUE);
|
||||||
|
|
||||||
|
List<DownloadEntity> filteredResults = new ArrayList<DownloadEntity>();
|
||||||
|
for (DownloadEntity entity : results)
|
||||||
|
{
|
||||||
|
Date createdDate = DefaultTypeConverter.INSTANCE.convert(Date.class, entity.getCreatedDate());
|
||||||
|
Date modifiedDate = DefaultTypeConverter.INSTANCE.convert(Date.class, entity.getModifiedDate());
|
||||||
|
|
||||||
|
if (modifiedDate == null)
|
||||||
|
{
|
||||||
|
modifiedDate = createdDate;
|
||||||
|
}
|
||||||
|
if (modifiedDate.before(paramsBean.getBefore()))
|
||||||
|
{
|
||||||
|
filteredResults.add(entity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isApplyPostQuerySorting()
|
||||||
|
{
|
||||||
|
// No post-query sorting. It's done within the queryAndFilter() method above.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download.cannedquery;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.query.CannedQuery;
|
||||||
|
import org.alfresco.query.CannedQueryFactory;
|
||||||
|
import org.alfresco.query.CannedQueryParameters;
|
||||||
|
import org.alfresco.repo.download.DownloadModel;
|
||||||
|
import org.alfresco.repo.query.AbstractQNameAwareCannedQueryFactory;
|
||||||
|
import org.alfresco.service.cmr.download.DownloadService;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.util.ParameterCheck;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link CannedQueryFactory} for queries relating to {@link DownloadEntity download entities}.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*
|
||||||
|
* @see DownloadService#deleteDownloads(Date)
|
||||||
|
*/
|
||||||
|
public class GetDownloadsCannedQueryFactory extends AbstractQNameAwareCannedQueryFactory<DownloadEntity>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception
|
||||||
|
{
|
||||||
|
super.afterPropertiesSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CannedQuery<DownloadEntity> getDownloadsCannedQuery(NodeRef containerNode, Date before)
|
||||||
|
{
|
||||||
|
ParameterCheck.mandatory("before", before);
|
||||||
|
|
||||||
|
GetDownloadsCannedQueryParams parameterBean = new GetDownloadsCannedQueryParams
|
||||||
|
(
|
||||||
|
getNodeId(containerNode),
|
||||||
|
getQNameId(ContentModel.PROP_NAME),
|
||||||
|
getQNameId(DownloadModel.TYPE_DOWNLOAD),
|
||||||
|
before
|
||||||
|
);
|
||||||
|
CannedQueryParameters params = new CannedQueryParameters(parameterBean);
|
||||||
|
|
||||||
|
final GetDownloadsCannedQuery cq = new GetDownloadsCannedQuery(
|
||||||
|
cannedQueryDAO, methodSecurity, params
|
||||||
|
);
|
||||||
|
|
||||||
|
return cq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see org.alfresco.query.CannedQueryFactory#getCannedQuery(org.alfresco.query.CannedQueryParameters)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CannedQuery<DownloadEntity> getCannedQuery(CannedQueryParameters parameters)
|
||||||
|
{
|
||||||
|
return new GetDownloadsCannedQuery(cannedQueryDAO, methodSecurity, parameters);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.download.cannedquery;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query parameters for GetDownloadsCannedQuery
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class GetDownloadsCannedQueryParams extends DownloadEntity
|
||||||
|
{
|
||||||
|
private Date before;
|
||||||
|
|
||||||
|
public GetDownloadsCannedQueryParams(Long parentNodeId, Long nameQNameId, Long contentTypeQNameId, Date before)
|
||||||
|
{
|
||||||
|
super(parentNodeId, nameQNameId, contentTypeQNameId);
|
||||||
|
this.before = before;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getBefore()
|
||||||
|
{
|
||||||
|
return before;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2005-2012 Alfresco Software, Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* License rights for this program may be obtained from Alfresco Software, Ltd.
|
||||||
|
* pursuant to a written agreement and any use of this program without such an
|
||||||
|
* agreement is prohibited.
|
||||||
|
*/
|
||||||
|
package org.alfresco.service.cmr.download;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DownloadRequest data transfer object.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public class DownloadRequest
|
||||||
|
{
|
||||||
|
private String owner;
|
||||||
|
private boolean recursive;
|
||||||
|
private List<AssociationRef> requestedNodes;
|
||||||
|
|
||||||
|
public DownloadRequest(boolean recursive, List<AssociationRef> requestedNodes, String owner)
|
||||||
|
{
|
||||||
|
this.owner = owner;
|
||||||
|
this.recursive = recursive;
|
||||||
|
this.requestedNodes = requestedNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AssociationRef> getRequetedNodes()
|
||||||
|
{
|
||||||
|
return requestedNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeRef[] getRequetedNodeRefs()
|
||||||
|
{
|
||||||
|
List<NodeRef> requestedNodeRefs = new ArrayList<NodeRef>(requestedNodes.size());
|
||||||
|
for (AssociationRef requestedNode : requestedNodes)
|
||||||
|
{
|
||||||
|
requestedNodeRefs.add(requestedNode.getTargetRef());
|
||||||
|
}
|
||||||
|
return requestedNodeRefs.toArray(new NodeRef[requestedNodeRefs.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getOwner()
|
||||||
|
{
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.service.cmr.download;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zip download service.
|
||||||
|
*
|
||||||
|
* Implementations are responsible for triggering the Zip creation process and
|
||||||
|
* reporting on the status of the of this process.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public interface DownloadService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Start the creation of a downlaodable archive file containing the content
|
||||||
|
* from the given nodeRefs.
|
||||||
|
*
|
||||||
|
* Implementations are expected to do this asynchronously, with clients
|
||||||
|
* using the returned NodeRef to check on progress.
|
||||||
|
|
||||||
|
* Initially, only zip files will be supported, however this could be
|
||||||
|
* extended in the future, to support additional archive types.
|
||||||
|
*
|
||||||
|
* @param nodeRefs NodeRefs of content to be added to the archive file
|
||||||
|
* @param recusirsive Recurse into container nodes
|
||||||
|
* @return Reference to node which will eventually contain the archive file
|
||||||
|
*/
|
||||||
|
public NodeRef createDownload(NodeRef[] nodeRefs, boolean recusirsive);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the status of the of the download identified by downloadNode.
|
||||||
|
*/
|
||||||
|
public DownloadStatus getDownloadStatus(NodeRef downloadNode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete downloads created before before.
|
||||||
|
*
|
||||||
|
* @param before
|
||||||
|
*/
|
||||||
|
public void deleteDownloads(Date before);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel a download request
|
||||||
|
*
|
||||||
|
* @param downloadNodeRef NodeRef of the download to cancel
|
||||||
|
*/
|
||||||
|
public void cancelDownload(NodeRef downloadNodeRef);
|
||||||
|
}
|
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.service.cmr.download;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immutable data transfer object representing the status of a download.
|
||||||
|
*
|
||||||
|
* Provides the current status and an indication of the progress. Prgress is
|
||||||
|
* measured by comparing done against total. Total gives an indication of the
|
||||||
|
* total work, while done indicates how much has been completed.
|
||||||
|
*
|
||||||
|
* @author amiller
|
||||||
|
*/
|
||||||
|
public class DownloadStatus implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 4513872550314507598L;
|
||||||
|
|
||||||
|
public enum Status {
|
||||||
|
PENDING,
|
||||||
|
IN_PROGRESS,
|
||||||
|
DONE,
|
||||||
|
MAX_CONTENT_SIZE_EXCEEDED,
|
||||||
|
CANCELLED
|
||||||
|
}
|
||||||
|
|
||||||
|
private long done;
|
||||||
|
private long total;
|
||||||
|
|
||||||
|
private long filesAddedCount;
|
||||||
|
private long totalFileCount;
|
||||||
|
|
||||||
|
private Status status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor
|
||||||
|
* @param status Current status of the download
|
||||||
|
* @param done Done count
|
||||||
|
* @param total Total to be de done
|
||||||
|
* @param filesAddedCount Number of files added to the archive
|
||||||
|
* @param totalFiles The number of files that will eventually be added to the archive
|
||||||
|
*/
|
||||||
|
public DownloadStatus(Status status, long done, long total, long filesAdded, long totalFiles)
|
||||||
|
{
|
||||||
|
this.status = status;
|
||||||
|
this.done = done;
|
||||||
|
this.total = total;
|
||||||
|
this.filesAddedCount = filesAdded;
|
||||||
|
this.totalFileCount = totalFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The percentage complete, calculated by multiplying done by 100 and dividing by total.
|
||||||
|
*/
|
||||||
|
public long getPercentageComplete()
|
||||||
|
{
|
||||||
|
return (done * 100) / total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if status is DONE, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isComplete()
|
||||||
|
{
|
||||||
|
return status == Status.DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the current status
|
||||||
|
*/
|
||||||
|
public Status getStatus()
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the current done count
|
||||||
|
*/
|
||||||
|
public long getDone()
|
||||||
|
{
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the total, to be done.
|
||||||
|
*/
|
||||||
|
public long getTotal()
|
||||||
|
{
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the total number of files in the download archive
|
||||||
|
*/
|
||||||
|
public long getTotalFiles()
|
||||||
|
{
|
||||||
|
return totalFileCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the number of files added to the download archive
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public long getFilesAdded()
|
||||||
|
{
|
||||||
|
return filesAddedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.service.cmr.download;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for updating the status of a download.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
public interface DownloadStatusUpdateService
|
||||||
|
{
|
||||||
|
|
||||||
|
void update(NodeRef nodeRef, DownloadStatus status, int sequenceNumber);
|
||||||
|
}
|
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Defines the contracts for creating archive files containing specified
|
||||||
|
* content from the repository.
|
||||||
|
*
|
||||||
|
* The DownlaodService is a client (Share) facing service responsible for
|
||||||
|
* creating a node containing enough information for the download archive to
|
||||||
|
* be created, and reporting on the progress of the creation process.
|
||||||
|
*
|
||||||
|
* @author Alex Miller
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.service.cmr.download;
|
Reference in New Issue
Block a user