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:
@@ -108,6 +108,7 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
|
||||
private AuthenticationContext authenticationContext;
|
||||
private ActionTrackingService actionTrackingService;
|
||||
private PolicyComponent policyComponent;
|
||||
private ActionServiceMonitor monitor;
|
||||
|
||||
/**
|
||||
* The asynchronous action execution queues map of name, queue
|
||||
@@ -201,6 +202,14 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
|
||||
{
|
||||
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
|
||||
@@ -706,8 +715,22 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
|
||||
actionTrackingService.recordActionExecuting(action);
|
||||
}
|
||||
|
||||
// Execute the action
|
||||
directActionExecution(action, actionedUponNodeRef);
|
||||
RunningAction runningAction = monitor.actionStarted(action);
|
||||
|
||||
try
|
||||
{
|
||||
// Execute the action
|
||||
directActionExecution(action, actionedUponNodeRef);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
runningAction.setException(e);
|
||||
throw e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
monitor.actionCompleted(runningAction);
|
||||
}
|
||||
|
||||
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