David Draper 642d332d24 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
2012-09-28 13:26:36 +00:00

256 lines
9.7 KiB
Java

/*
* 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);
}
}