diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml
index 00ea36bafd..0e40a6109b 100644
--- a/config/alfresco/bootstrap-context.xml
+++ b/config/alfresco/bootstrap-context.xml
@@ -602,8 +602,9 @@
-
+
+
diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml
index 3c61f923ac..ef0490b80f 100644
--- a/config/alfresco/public-services-context.xml
+++ b/config/alfresco/public-services-context.xml
@@ -1419,6 +1419,38 @@
+
+
+ org.alfresco.service.cmr.action.scheduled.ScheduledPersistedActionService
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${server.transaction.mode.default}
+
+
+
+
+
+
org.alfresco.service.cmr.security.PublicServiceAccessService
diff --git a/source/java/org/alfresco/repo/action/ActionModel.java b/source/java/org/alfresco/repo/action/ActionModel.java
index 48d0f7401b..ddb711999b 100644
--- a/source/java/org/alfresco/repo/action/ActionModel.java
+++ b/source/java/org/alfresco/repo/action/ActionModel.java
@@ -51,6 +51,12 @@ public interface ActionModel
static final QName ASPECT_ACTIONS = QName.createQName(ACTION_MODEL_URI, "actions");
static final QName ASSOC_ACTION_FOLDER = QName.createQName(ACTION_MODEL_URI, "actionFolder");
+ static final QName TYPE_ACTION_SCHEDULE = QName.createQName(ACTION_MODEL_URI, "actionSchedule");
+ static final QName PROP_START_DATE = QName.createQName(ACTION_MODEL_URI, "startDate");
+ static final QName PROP_INTERVAL_COUNT = QName.createQName(ACTION_MODEL_URI, "intervalCount");
+ static final QName PROP_INTERVAL_PERIOD = QName.createQName(ACTION_MODEL_URI, "intervalPeriod");
+ static final QName ASSOC_SCHEDULED_ACTION = QName.createQName(ACTION_MODEL_URI, "scheduledAction");
+
//static final QName ASPECT_ACTIONABLE = QName.createQName(ACTION_MODEL_URI, "actionable");
//static final QName ASSOC_SAVED_ACTION_FOLDERS = QName.createQName(ACTION_MODEL_URI, "savedActionFolders");
//static final QName TYPE_SAVED_ACTION_FOLDER = QName.createQName(ACTION_MODEL_URI, "savedactionfolder");
diff --git a/source/java/org/alfresco/repo/action/actionModel.xml b/source/java/org/alfresco/repo/action/actionModel.xml
index 7dfd586b15..71c186f4ab 100644
--- a/source/java/org/alfresco/repo/action/actionModel.xml
+++ b/source/java/org/alfresco/repo/action/actionModel.xml
@@ -235,11 +235,12 @@
true
- M
- W
- D
- h
- m
+ Month
+ Week
+ Day
+ Hour
+ Minute
+ Second
@@ -248,6 +249,10 @@
+
+ false
+ false
+
act:action
true
diff --git a/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionImpl.java b/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionImpl.java
index 53d9873278..08b7616f38 100644
--- a/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionImpl.java
+++ b/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionImpl.java
@@ -75,7 +75,7 @@ public class ScheduledPersistedActionImpl implements ScheduledPersistedAction
/** Get where the action lives */
public NodeRef getActionNodeRef()
{
- return action.getNodeRef();
+ return action == null ? null : action.getNodeRef();
}
/**
@@ -157,8 +157,8 @@ public class ScheduledPersistedActionImpl implements ScheduledPersistedAction
/**
- * Returns the interval in a form like 1D (1 day)
- * or 2h (2 hours), or null if a period+count
+ * Returns the interval in a form like 1Day (1 day)
+ * or 2Hour (2 hours), or null if a period+count
* hasn't been set
*/
public String getScheduleInterval()
@@ -167,7 +167,7 @@ public class ScheduledPersistedActionImpl implements ScheduledPersistedAction
{
return null;
}
- return intervalCount.toString() + intervalPeriod.getLetter();
+ return intervalCount.toString() + intervalPeriod.name();
}
/**
diff --git a/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceImpl.java b/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceImpl.java
index 7459dfb5b4..61324135b3 100644
--- a/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceImpl.java
+++ b/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceImpl.java
@@ -20,6 +20,7 @@ package org.alfresco.repo.action.scheduled;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -29,14 +30,22 @@ import org.alfresco.repo.action.ActionModel;
import org.alfresco.repo.action.RuntimeActionService;
import org.alfresco.repo.model.Repository;
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.ActionService;
import org.alfresco.service.cmr.action.scheduled.ScheduledPersistedAction;
import org.alfresco.service.cmr.action.scheduled.ScheduledPersistedActionService;
+import org.alfresco.service.cmr.action.scheduled.ScheduledPersistedAction.IntervalPeriod;
+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.QName;
+import org.alfresco.service.namespace.RegexQNamePattern;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
@@ -66,7 +75,7 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc
protected static NodeRef SCHEDULED_ACTION_ROOT_NODE_REF;
protected static final Set ACTION_TYPES = new HashSet(Arrays
- .asList(new QName[] { ActionModel.TYPE_ACTION })); // TODO
+ .asList(new QName[] { ActionModel.TYPE_ACTION_SCHEDULE }));
protected static final String SCHEDULER_GROUP = "PersistedActions";
@@ -113,7 +122,7 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc
NodeRef dataDictionary = startupNodeService.getChildByName(
repositoryHelper.getCompanyHome(),
ContentModel.ASSOC_CONTAINS,
- "data dictionary"
+ "Data Dictionary"
);
SCHEDULED_ACTION_ROOT_NODE_REF = startupNodeService.getChildByName(
dataDictionary,
@@ -151,40 +160,133 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc
*/
public void saveSchedule(ScheduledPersistedAction schedule)
{
+ ScheduledPersistedActionImpl scheduleImpl = (ScheduledPersistedActionImpl)schedule;
+
// Remove if already there
- removeFromScheduler((ScheduledPersistedActionImpl) schedule);
+ removeFromScheduler(scheduleImpl);
- // TODO Create the node + relationship if not already there
-
- // Save to the repo
- // TODO Persist details
+ if (scheduleImpl.getPersistedAtNodeRef() == null)
+ {
+ // if not already persisted, create the persistent schedule
+ createPersistentSchedule(scheduleImpl);
+ }
+ // update the persistent schedule with schedule properties
+ updatePersistentSchedule(scheduleImpl);
+
// Add to the scheduler again
- addToScheduler((ScheduledPersistedActionImpl) schedule);
+ addToScheduler(scheduleImpl);
}
+ private void createPersistentSchedule(ScheduledPersistedActionImpl schedule)
+ {
+ ChildAssociationRef childAssoc = nodeService.createNode(SCHEDULED_ACTION_ROOT_NODE_REF,
+ ContentModel.ASSOC_CONTAINS, QName.createQName(GUID.generate()),
+ ActionModel.TYPE_ACTION_SCHEDULE);
+ schedule.setPersistedAtNodeRef(childAssoc.getChildRef());
+ }
+
+ private void updatePersistentSchedule(ScheduledPersistedActionImpl schedule)
+ {
+ NodeRef nodeRef = schedule.getPersistedAtNodeRef();
+ if (nodeRef == null)
+ throw new IllegalStateException("Must be persisted first");
+
+ // update schedule properties
+ nodeService.setProperty(nodeRef, ActionModel.PROP_START_DATE, schedule.getScheduleStart());
+ nodeService.setProperty(nodeRef, ActionModel.PROP_INTERVAL_COUNT, schedule.getScheduleIntervalCount());
+ IntervalPeriod period = schedule.getScheduleIntervalPeriod();
+ nodeService.setProperty(nodeRef, ActionModel.PROP_INTERVAL_PERIOD, period == null ? null : period.name());
+
+ // update scheduled action (represented as an association)
+ // NOTE: can only associate to a single action from a schedule (as specified by the action model)
+
+ // update association to reflect updated schedule
+ AssociationRef actionAssoc = findActionAssociationFromSchedule(nodeRef);
+ NodeRef actionNodeRef = schedule.getActionNodeRef();
+ if (actionNodeRef == null)
+ {
+ if (actionAssoc != null)
+ {
+ // remove associated action
+ nodeService.removeAssociation(actionAssoc.getSourceRef(), actionAssoc.getTargetRef(), actionAssoc.getTypeQName());
+ }
+ }
+ else
+ {
+ if (actionAssoc == null)
+ {
+ // create associated action
+ nodeService.createAssociation(nodeRef, actionNodeRef, ActionModel.ASSOC_SCHEDULED_ACTION);
+ }
+ else if (!actionAssoc.getTargetRef().equals(actionNodeRef))
+ {
+ // associated action has changed... first remove existing association
+ nodeService.removeAssociation(actionAssoc.getSourceRef(), actionAssoc.getTargetRef(), actionAssoc.getTypeQName());
+ nodeService.createAssociation(nodeRef, actionNodeRef, ActionModel.ASSOC_SCHEDULED_ACTION);
+ }
+ }
+ }
+
/**
* Removes the schedule for the action, and cancels future executions of it.
* The persisted action is unchanged.
*/
public void deleteSchedule(ScheduledPersistedAction schedule)
{
+ ScheduledPersistedActionImpl scheduleImpl = (ScheduledPersistedActionImpl)schedule;
// Remove from the scheduler
- removeFromScheduler((ScheduledPersistedActionImpl) schedule);
+ removeFromScheduler(scheduleImpl);
// Now remove from the repo
- // TODO
+ deletePersistentSchedule(scheduleImpl);
}
+ private void deletePersistentSchedule(ScheduledPersistedActionImpl schedule)
+ {
+ NodeRef nodeRef = schedule.getPersistedAtNodeRef();
+ if (nodeRef == null)
+ return;
+
+ // NOTE: this will also cascade delete action association
+ nodeService.deleteNode(nodeRef);
+
+ schedule.setPersistedAtNodeRef(null);
+ }
+
/**
* Returns the schedule for the specified action, or null if it isn't
* currently scheduled.
*/
public ScheduledPersistedAction getSchedule(Action persistedAction)
{
- // TODO look for a relationship of the special type from
- // the action, then if we find it load the schedule via it
- return null;
+ NodeRef nodeRef = persistedAction.getNodeRef();
+ if (nodeRef == null)
+ {
+ // action is not persistent
+ return null;
+ }
+
+ // locate associated schedule for action
+ List assocs = nodeService.getSourceAssocs(nodeRef, RegexQNamePattern.MATCH_ALL);
+ AssociationRef scheduledAssoc = null;
+ for (AssociationRef assoc : assocs)
+ {
+ if (ActionModel.ASSOC_SCHEDULED_ACTION.equals(assoc.getTypeQName()))
+ {
+ scheduledAssoc = assoc;
+ break;
+ }
+ }
+
+ if (scheduledAssoc == null)
+ {
+ // there is no associated schedule
+ return null;
+ }
+
+ // load the scheduled action
+ return loadPersistentSchedule(scheduledAssoc.getSourceRef());
}
/**
@@ -204,15 +306,56 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc
childAssocs.size());
for (ChildAssociationRef actionAssoc : childAssocs)
{
- // TODO
- // Action nextAction =
- // runtimeActionService.createAction(actionAssoc.getChildRef());
- // renderingActions.add(new ReplicationDefinitionImpl(nextAction));
+ ScheduledPersistedActionImpl scheduleImpl = loadPersistentSchedule(actionAssoc.getChildRef());
+ scheduledActions.add(scheduleImpl);
}
return scheduledActions;
}
+ protected ScheduledPersistedActionImpl loadPersistentSchedule(NodeRef schedule)
+ {
+ if (!nodeService.exists(schedule))
+ return null;
+
+ // create action
+ Action action = null;
+ AssociationRef actionAssoc = findActionAssociationFromSchedule(schedule);
+ if (actionAssoc != null)
+ {
+ action = runtimeActionService.createAction(actionAssoc.getTargetRef());
+ }
+
+ // create schedule
+ ScheduledPersistedActionImpl scheduleImpl = new ScheduledPersistedActionImpl(action);
+ scheduleImpl.setPersistedAtNodeRef(schedule);
+ scheduleImpl.setScheduleStart((Date)nodeService.getProperty(schedule, ActionModel.PROP_START_DATE));
+ scheduleImpl.setScheduleIntervalCount((Integer)nodeService.getProperty(schedule, ActionModel.PROP_INTERVAL_COUNT));
+ String period = (String)nodeService.getProperty(schedule, ActionModel.PROP_INTERVAL_PERIOD);
+ if (period != null)
+ {
+ scheduleImpl.setScheduleIntervalPeriod(IntervalPeriod.valueOf(period));
+ }
+
+ return scheduleImpl;
+ }
+
+ private AssociationRef findActionAssociationFromSchedule(NodeRef schedule)
+ {
+ List assocs = nodeService.getTargetAssocs(schedule, RegexQNamePattern.MATCH_ALL);
+ AssociationRef actionAssoc = null;
+ for (AssociationRef assoc : assocs)
+ {
+ if (ActionModel.ASSOC_SCHEDULED_ACTION.equals(assoc.getTypeQName()))
+ {
+ actionAssoc = assoc;
+ break;
+ }
+ }
+
+ return actionAssoc;
+ }
+
/**
* Takes an entry out of the scheduler, if it's currently there.
*/
@@ -287,15 +430,36 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc
public static class ScheduledPersistedActionServiceBootstrap extends AbstractLifecycleBean
{
private ScheduledPersistedActionServiceImpl service;
+ private RetryingTransactionHelper txnHelper;
+
public void setScheduledPersistedActionService(ScheduledPersistedActionServiceImpl scheduledPersistedActionService)
{
this.service = scheduledPersistedActionService;
}
+ public void setTransactionHelper(RetryingTransactionHelper txnHelper)
+ {
+ this.txnHelper = txnHelper;
+ }
+
public void onBootstrap(ApplicationEvent event)
{
- service.locatePersistanceFolder();
- service.schedulePreviouslyPersisted();
+ AuthenticationUtil.runAs(new RunAsWork