From 2273c26793fe6e3abfd90a400823e8e46b3303bb Mon Sep 17 00:00:00 2001
From: abobrov <abobrov@localhost>
Date: Mon, 15 Dec 2008 16:07:29 +0000
Subject: [PATCH] - [Issue 274] Recurring Tasks
---
opends/resource/schema/02-config.ldif | 12
opends/src/server/org/opends/server/tools/tasks/TaskEntry.java | 14
opends/src/server/org/opends/server/backends/task/TaskState.java | 32 +
opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java | 17
opends/src/server/org/opends/server/backends/task/Task.java | 12
opends/src/messages/messages/backend.properties | 41 +
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/task/TaskBackendTestCase.java | 131 +++++
opends/src/server/org/opends/server/tasks/BackupTask.java | 10
opends/src/server/org/opends/server/tools/ToolConstants.java | 10
opends/src/messages/messages/tools.properties | 8
opends/src/server/org/opends/server/tools/RestoreDB.java | 11
opends/src/server/org/opends/server/backends/task/TaskScheduler.java | 184 ++++++-
opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java | 65 +
opends/src/server/org/opends/server/tools/BackUpDB.java | 11
opends/src/server/org/opends/server/tools/ImportLDIF.java | 8
opends/src/server/org/opends/server/tools/ManageTasks.java | 40 +
opends/src/server/org/opends/server/backends/task/TaskThread.java | 7
opends/src/server/org/opends/server/tools/ExportLDIF.java | 8
opends/src/server/org/opends/server/tools/tasks/TaskTool.java | 135 +++--
opends/src/messages/messages/task.properties | 1
opends/src/server/org/opends/server/tools/tasks/TaskClient.java | 94 +++
opends/src/server/org/opends/server/backends/task/RecurringTask.java | 406 +++++++++++++++-
opends/src/server/org/opends/server/backends/task/TaskBackend.java | 140 +++--
opends/src/server/org/opends/server/config/ConfigConstants.java | 8
24 files changed, 1,169 insertions(+), 236 deletions(-)
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index e2a9fc0..137ba88 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -311,8 +311,8 @@
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
X-ORIGIN 'OpenDS Directory Server' )
-attributeTypes: ( 1.3.6.1.4.1.26027.1.1.60
- NAME 'ds-recurring-task-class-name'
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.515
+ NAME 'ds-recurring-task-schedule'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
X-ORIGIN 'OpenDS Directory Server' )
@@ -2728,14 +2728,12 @@
ds-cfg-profile-sample-interval $
ds-cfg-profile-action )
X-ORIGIN 'OpenDS Directory Server' )
-objectClasses: ( 1.3.6.1.4.1.26027.1.2.38
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.198
NAME 'ds-recurring-task'
SUP top
- STRUCTURAL
- MUST ( ds-recurring-task-class-name $
+ AUXILIARY
+ MUST ( ds-recurring-task-schedule $
ds-recurring-task-id )
- MAY ( ds-task-notify-on-completion $
- ds-task-notify-on-error )
X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.39
NAME 'ds-cfg-root-config'
diff --git a/opends/src/messages/messages/backend.properties b/opends/src/messages/messages/backend.properties
index 2839dd2..48e4025 100644
--- a/opends/src/messages/messages/backend.properties
+++ b/opends/src/messages/messages/backend.properties
@@ -318,18 +318,18 @@
SEVERE_ERR_RECURRINGTASK_MULTIPLE_ID_VALUES_103=The provided recurring task \
entry contains multiple values for the %s attribute, which is used to specify \
the recurring task ID, but only a single value is allowed
-SEVERE_ERR_RECURRINGTASK_NO_CLASS_ATTRIBUTE_104=The provided recurring task \
- entry does not contain attribute %s which is needed to specify the \
- fully-qualified name of the class providing the task logic
-SEVERE_ERR_RECURRINGTASK_MULTIPLE_CLASS_TYPES_105=The provided recurring task \
- entry contains multiple attributes with type %s, which is used to hold the \
- task class name, but only a single instance is allowed
-SEVERE_ERR_RECURRINGTASK_NO_CLASS_VALUES_106=The provided recurring task \
+SEVERE_ERR_RECURRINGTASK_NO_SCHEDULE_ATTRIBUTE_104=The provided recurring task \
+ entry does not contain attribute %s which is needed to specify recurring task \
+ schedule
+SEVERE_ERR_RECURRINGTASK_MULTIPLE_SCHEDULE_TYPES_105=The provided recurring \
+ task entry contains multiple attributes with type %s, which is used to hold \
+ recurring task schedule, but only a single instance is allowed
+SEVERE_ERR_RECURRINGTASK_NO_SCHEDULE_VALUES_106=The provided recurring task \
entry does not contain any values for the %s attribute, which is used to \
- specify the fully-qualified name of the class providing the task logic
-SEVERE_ERR_RECURRINGTASK_MULTIPLE_CLASS_VALUES_107=The provided recurring \
+ specify recurring task schedule
+SEVERE_ERR_RECURRINGTASK_MULTIPLE_SCHEDULE_VALUES_107=The provided recurring \
task entry contains multiple values for the %s attribute, which is used to \
- specify the task class name, but only a single value is allowed
+ specify recurring task schedule, but only a single value is allowed
SEVERE_ERR_RECURRINGTASK_CANNOT_LOAD_CLASS_108=An error occurred while \
attempting to load class %s specified in attribute %s of the provided \
recurring task entry: %s. Does this class exist in the Directory Server \
@@ -1035,3 +1035,24 @@
MILD_ERR_NUM_SUBORDINATES_NOT_SUPPORTED_369=This backend does not provide \
support for the numSubordinates operational attribute
NOTICE_BACKEND_OFFLINE_370=The backend %s is now taken offline
+SEVERE_ERR_RECURRINGTASK_INVALID_N_TOKENS_371=The provided recurring task \
+ entry attribute %s holding the recurring task schedule has invalid number \
+ of tokens
+SEVERE_ERR_RECURRINGTASK_INVALID_MINUTE_TOKEN_372=The provided recurring task \
+ entry attribute %s holding the recurring task schedule has invalid minute \
+ token
+SEVERE_ERR_RECURRINGTASK_INVALID_HOUR_TOKEN_373=The provided recurring task \
+ entry attribute %s holding the recurring task schedule has invalid hour \
+ token
+SEVERE_ERR_RECURRINGTASK_INVALID_DAY_TOKEN_374=The provided recurring task \
+ entry attribute %s holding the recurring task schedule has invalid day of \
+ the month token
+SEVERE_ERR_RECURRINGTASK_INVALID_MONTH_TOKEN_375=The provided recurring task \
+ entry attribute %s holding the recurring task schedule has invalid month of \
+ the year token
+SEVERE_ERR_RECURRINGTASK_INVALID_WEEKDAY_TOKEN_376=The provided recurring task \
+ entry attribute %s holding the recurring task schedule has invalid day of the \
+ week token
+SEVERE_ERR_RECURRINGTASK_INVALID_TOKENS_COMBO_377=The provided recurring task \
+ entry attribute %s holding the recurring task schedule has invalid tokens \
+ combination yielding a nonexistent calendar date
diff --git a/opends/src/messages/messages/task.properties b/opends/src/messages/messages/task.properties
index c8ce673..c1e756a 100644
--- a/opends/src/messages/messages/task.properties
+++ b/opends/src/messages/messages/task.properties
@@ -193,3 +193,4 @@
INFO_IMPORT_ARG_RANDOM_SEED_105=Random Seed
SEVERE_ERR_TASK_LDAP_FAILED_TO_CONNECT_WRONG_PORT_106=Unable to connect to the \
server at %s on port %s. Check this port is an administration port
+INFO_TASK_STATE_RECURRING_107=Recurring
diff --git a/opends/src/messages/messages/tools.properties b/opends/src/messages/messages/tools.properties
index cada3b3..d6d99ee 100644
--- a/opends/src/messages/messages/tools.properties
+++ b/opends/src/messages/messages/tools.properties
@@ -2144,7 +2144,7 @@
be canceled
INFO_TASKINFO_NO_CANCELABLE_TASKS_1452=There are currently no cancelable tasks
SEVERE_ERR_TASK_CLIENT_UNKNOWN_TASK_1453=There are no tasks defined with ID '%s'
-SEVERE_ERR_TASK_CLIENT_UNCANCELABLE_TASK_1454=Task '%s' is has finished and \
+SEVERE_ERR_TASK_CLIENT_UNCANCELABLE_TASK_1454=Task '%s' has finished and \
cannot be canceled
SEVERE_ERR_TASK_CLIENT_TASK_STATE_UNKNOWN_1455=State for task '%s' cannot be \
determined
@@ -2431,3 +2431,9 @@
cannot be backuped is the directory %s: this directory is already a backup \
directory for backend %s
+INFO_RECURRING_TASK_PLACEHOLDER_1651={schedulePattern}
+INFO_DESCRIPTION_RECURRING_TASK_1652=Indicates the task is recurring and will \
+ be scheduled according to the value argument expressed in crontab(5) \
+ compatible time/date pattern
+INFO_TASK_TOOL_RECURRING_TASK_SCHEDULED_1653=Recurring %s task %s scheduled \
+ successfully
diff --git a/opends/src/server/org/opends/server/backends/task/RecurringTask.java b/opends/src/server/org/opends/server/backends/task/RecurringTask.java
index ab117ae..67fcec9 100644
--- a/opends/src/server/org/opends/server/backends/task/RecurringTask.java
+++ b/opends/src/server/org/opends/server/backends/task/RecurringTask.java
@@ -25,6 +25,10 @@
* Copyright 2006-2008 Sun Microsystems, Inc.
*/
package org.opends.server.backends.task;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.GregorianCalendar;
import org.opends.messages.Message;
@@ -32,6 +36,8 @@
import java.util.Iterator;
import java.util.List;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
@@ -45,9 +51,13 @@
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.Attributes;
import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.RDN;
+
import static org.opends.messages.BackendMessages.*;
import static org.opends.server.util.StaticUtils.*;
+import static org.opends.server.util.ServerConstants.*;
@@ -78,7 +88,38 @@
// class.
private String taskClassName;
+ // Task instance.
+ private Task task;
+ // Task scheduler for this task.
+ private TaskScheduler taskScheduler;
+
+ // Number of tokens in the task schedule tab.
+ private static final int TASKTAB_NUM_TOKENS = 5;
+
+ /**
+ * Task tab fields.
+ */
+ private static enum TaskTab {MINUTE, HOUR, DAY, MONTH, WEEKDAY};
+
+ // Exact match pattern.
+ private static final Pattern exactPattern =
+ Pattern.compile("\\d+");
+
+ // Range match pattern.
+ private static final Pattern rangePattern =
+ Pattern.compile("\\d+[-]\\d+");
+
+ // List match pattern.
+ private static final Pattern listPattern =
+ Pattern.compile("^(\\d+,)(.*)(\\d+)$");
+
+ // Boolean arrays holding task tab slots.
+ private boolean[] minutesArray;
+ private boolean[] hoursArray;
+ private boolean[] daysArray;
+ private boolean[] monthArray;
+ private boolean[] weekdayArray;
/**
* Creates a new recurring task based on the information in the provided
@@ -95,10 +136,10 @@
public RecurringTask(TaskScheduler taskScheduler, Entry recurringTaskEntry)
throws DirectoryException
{
- this.recurringTaskEntry = recurringTaskEntry;
+ this.taskScheduler = taskScheduler;
+ this.recurringTaskEntry = recurringTaskEntry;
this.recurringTaskEntryDN = recurringTaskEntry.getDN();
-
// Get the recurring task ID from the entry. If there isn't one, then fail.
AttributeType attrType = DirectoryServer.getAttributeType(
ATTR_RECURRING_TASK_ID.toLowerCase());
@@ -142,39 +183,35 @@
recurringTaskID = value.getStringValue();
- // FIXME -- Need to have some method of getting the scheduling information
- // from the recurring task entry.
-
-
- // Get the class name from the entry. If there isn't one, then fail.
+ // Get the schedule for this task.
attrType = DirectoryServer.getAttributeType(
- ATTR_RECURRING_TASK_CLASS_NAME.toLowerCase());
+ ATTR_RECURRING_TASK_SCHEDULE.toLowerCase());
if (attrType == null)
{
attrType = DirectoryServer.getDefaultAttributeType(
- ATTR_RECURRING_TASK_CLASS_NAME);
+ ATTR_RECURRING_TASK_SCHEDULE);
}
attrList = recurringTaskEntry.getAttribute(attrType);
if ((attrList == null) || attrList.isEmpty())
{
- Message message = ERR_RECURRINGTASK_NO_CLASS_ATTRIBUTE.get(
- ATTR_RECURRING_TASK_CLASS_NAME);
+ Message message = ERR_RECURRINGTASK_NO_SCHEDULE_ATTRIBUTE.get(
+ ATTR_RECURRING_TASK_SCHEDULE);
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
}
- if (attrList.size() > 0)
+ if (attrList.size() > 1)
{
- Message message = ERR_RECURRINGTASK_MULTIPLE_CLASS_TYPES.get(
- ATTR_RECURRING_TASK_CLASS_NAME);
+ Message message = ERR_RECURRINGTASK_MULTIPLE_SCHEDULE_TYPES.get(
+ ATTR_RECURRING_TASK_SCHEDULE);
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
}
attr = attrList.get(0);
if (attr.isEmpty())
{
- Message message =
- ERR_RECURRINGTASK_NO_CLASS_VALUES.get(ATTR_RECURRING_TASK_CLASS_NAME);
+ Message message = ERR_RECURRINGTASK_NO_SCHEDULE_VALUES.get(
+ ATTR_RECURRING_TASK_SCHEDULE);
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
}
@@ -182,8 +219,51 @@
value = iterator.next();
if (iterator.hasNext())
{
- Message message = ERR_RECURRINGTASK_MULTIPLE_CLASS_VALUES.get(
- ATTR_RECURRING_TASK_CLASS_NAME);
+ Message message = ERR_RECURRINGTASK_MULTIPLE_SCHEDULE_VALUES.get(
+ ATTR_RECURRING_TASK_SCHEDULE);
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
+ }
+
+ String taskScheduleTab = value.getStringValue();
+ parseTaskTab(taskScheduleTab);
+
+ // Get the class name from the entry. If there isn't one, then fail.
+ attrType = DirectoryServer.getAttributeType(
+ ATTR_TASK_CLASS.toLowerCase());
+ if (attrType == null)
+ {
+ attrType = DirectoryServer.getDefaultAttributeType(ATTR_TASK_CLASS);
+ }
+
+ attrList = recurringTaskEntry.getAttribute(attrType);
+ if ((attrList == null) || attrList.isEmpty())
+ {
+ Message message = ERR_TASKSCHED_NO_CLASS_ATTRIBUTE.get(
+ ATTR_TASK_CLASS);
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
+ }
+
+ if (attrList.size() > 1)
+ {
+ Message message = ERR_TASKSCHED_MULTIPLE_CLASS_TYPES.get(
+ ATTR_TASK_CLASS);
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
+ }
+
+ attr = attrList.get(0);
+ if (attr.isEmpty())
+ {
+ Message message =
+ ERR_TASKSCHED_NO_CLASS_VALUES.get(ATTR_TASK_CLASS);
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
+ }
+
+ iterator = attr.iterator();
+ value = iterator.next();
+ if (iterator.hasNext())
+ {
+ Message message = ERR_TASKSCHED_MULTIPLE_CLASS_VALUES.get(
+ ATTR_TASK_CLASS);
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
}
@@ -204,7 +284,7 @@
}
Message message = ERR_RECURRINGTASK_CANNOT_LOAD_CLASS.
- get(String.valueOf(taskClassName), ATTR_RECURRING_TASK_CLASS_NAME,
+ get(String.valueOf(taskClassName), ATTR_TASK_CLASS,
getExceptionMessage(e));
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
e);
@@ -212,7 +292,6 @@
// Make sure that the specified class can be instantiated as a task.
- Task task;
try
{
task = (Task) taskClass.newInstance();
@@ -308,12 +387,289 @@
/**
* Schedules the next iteration of this recurring task for processing.
*
- * @return The task that has been scheduled for processing.
+ * @return The task that has been scheduled for processing.
+ * @throws DirectoryException to indicate an error.
*/
- public Task scheduleNextIteration()
+ public Task scheduleNextIteration() throws DirectoryException
{
- // NYI
- return null;
+ Task nextTask = null;
+ Date nextTaskDate = null;
+
+ try {
+ nextTaskDate = getNextIteration();
+ } catch (IllegalArgumentException e) {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_RECURRINGTASK_INVALID_TOKENS_COMBO.get(
+ ATTR_RECURRING_TASK_SCHEDULE));
+ }
+
+ SimpleDateFormat dateFormat = new SimpleDateFormat(
+ DATE_FORMAT_COMPACT_LOCAL_TIME);
+ String nextTaskStartTime = dateFormat.format(nextTaskDate);
+
+ try {
+ // Make a regular task iteration from this recurring task.
+ nextTask = task.getClass().newInstance();
+ Entry nextTaskEntry = recurringTaskEntry.duplicate(false);
+ String nextTaskID = task.getTaskID() + " - " +
+ nextTaskDate.toString();
+ String nextTaskIDName = NAME_PREFIX_TASK + "id";
+ AttributeType taskIDAttrType =
+ DirectoryServer.getAttributeType(nextTaskIDName);
+ Attribute nextTaskIDAttr = Attributes.create(
+ taskIDAttrType, nextTaskID);
+ nextTaskEntry.replaceAttribute(nextTaskIDAttr);
+ RDN nextTaskRDN = RDN.decode(nextTaskIDName + "=" + nextTaskID);
+ DN nextTaskDN = new DN(nextTaskRDN,
+ taskScheduler.getTaskBackend().getScheduledTasksParentDN());
+ nextTaskEntry.setDN(nextTaskDN);
+
+ String nextTaskStartTimeName = NAME_PREFIX_TASK +
+ "scheduled-start-time";
+ AttributeType taskStartTimeAttrType =
+ DirectoryServer.getAttributeType(nextTaskStartTimeName);
+ Attribute nextTaskStartTimeAttr = Attributes.create(
+ taskStartTimeAttrType, nextTaskStartTime);
+ nextTaskEntry.replaceAttribute(nextTaskStartTimeAttr);
+
+ nextTask.initializeTaskInternal(taskScheduler, nextTaskEntry);
+ nextTask.initializeTask();
+ } catch (Exception e) {
+ // Should not happen, debug log it otherwise.
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+
+ return nextTask;
+ }
+
+ /**
+ * Parse and validate recurring task schedule.
+ * @param taskSchedule recurring task schedule tab in crontab(5) format.
+ * @throws DirectoryException to indicate an error.
+ */
+ private void parseTaskTab(String taskSchedule) throws DirectoryException
+ {
+ StringTokenizer st = new StringTokenizer(taskSchedule);
+
+ if (st.countTokens() != TASKTAB_NUM_TOKENS) {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_RECURRINGTASK_INVALID_N_TOKENS.get(
+ ATTR_RECURRING_TASK_SCHEDULE));
+ }
+
+ for (TaskTab taskTabToken : TaskTab.values()) {
+ String token = st.nextToken();
+ switch (taskTabToken) {
+ case MINUTE:
+ try {
+ minutesArray = parseTaskTabField(token, 0, 59);
+ } catch (IllegalArgumentException e) {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_RECURRINGTASK_INVALID_MINUTE_TOKEN.get(
+ ATTR_RECURRING_TASK_SCHEDULE));
+ }
+ break;
+ case HOUR:
+ try {
+ hoursArray = parseTaskTabField(token, 0, 23);
+ } catch (IllegalArgumentException e) {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_RECURRINGTASK_INVALID_HOUR_TOKEN.get(
+ ATTR_RECURRING_TASK_SCHEDULE));
+ }
+ break;
+ case DAY:
+ try {
+ daysArray = parseTaskTabField(token, 1, 31);
+ } catch (IllegalArgumentException e) {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_RECURRINGTASK_INVALID_DAY_TOKEN.get(
+ ATTR_RECURRING_TASK_SCHEDULE));
+ }
+ break;
+ case MONTH:
+ try {
+ monthArray = parseTaskTabField(token, 1, 12);
+ } catch (IllegalArgumentException e) {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_RECURRINGTASK_INVALID_MONTH_TOKEN.get(
+ ATTR_RECURRING_TASK_SCHEDULE));
+ }
+ break;
+ case WEEKDAY:
+ try {
+ weekdayArray = parseTaskTabField(token, 0, 6);
+ } catch (IllegalArgumentException e) {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_RECURRINGTASK_INVALID_WEEKDAY_TOKEN.get(
+ ATTR_RECURRING_TASK_SCHEDULE));
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parse and validate recurring task schedule field.
+ * @param tabField recurring task schedule field in crontab(5) format.
+ * @param minValue minimum value allowed for this field.
+ * @param maxValue maximum value allowed for this field.
+ * @return boolean schedule slots range set according to
+ * the schedule field.
+ * @throws IllegalArgumentException if tab field is invalid.
+ */
+ private boolean[] parseTaskTabField(String tabField,
+ int minValue, int maxValue) throws IllegalArgumentException
+ {
+ boolean[] valueList = new boolean[maxValue + 1];
+ Arrays.fill(valueList, false);
+
+ // Blanket.
+ if (tabField.equals("*")) {
+ for (int i = minValue; i <= maxValue; i++) {
+ valueList[i] = true;
+ }
+ return valueList;
+ }
+
+ // Exact.
+ if (exactPattern.matcher(tabField).matches()) {
+ int value = Integer.parseInt(tabField);
+ if ((value >= minValue) && (value <= maxValue)) {
+ valueList[value] = true;
+ return valueList;
+ }
+ throw new IllegalArgumentException();
+ }
+
+ // Range.
+ if (rangePattern.matcher(tabField).matches()) {
+ StringTokenizer st = new StringTokenizer(tabField, "-");
+ int startValue = Integer.parseInt(st.nextToken());
+ int endValue = Integer.parseInt(st.nextToken());
+ if ((startValue < endValue) &&
+ ((startValue >= minValue) && (endValue <= maxValue)))
+ {
+ for (int i = startValue; i <= endValue; i++) {
+ valueList[i] = true;
+ }
+ return valueList;
+ }
+ throw new IllegalArgumentException();
+ }
+
+ // List.
+ if (listPattern.matcher(tabField).matches()) {
+ StringTokenizer st = new StringTokenizer(tabField, ",");
+ while (st.hasMoreTokens()) {
+ int value = Integer.parseInt(st.nextToken());
+ if ((value >= minValue) && (value <= maxValue)) {
+ valueList[value] = true;
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+ return valueList;
+ }
+
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Get next reccuring slot from the range.
+ * @param timesList the range.
+ * @param fromNow the current slot.
+ * @return next recurring slot in the range.
+ */
+ private int getNextTimeSlice(boolean[] timesList, int fromNow)
+ {
+ for (int i = fromNow; i < timesList.length; i++) {
+ if (timesList[i]) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Get next task iteration date according to recurring schedule.
+ * @return next task iteration date.
+ * @throws IllegalArgumentException if recurring schedule is invalid.
+ */
+ private Date getNextIteration() throws IllegalArgumentException
+ {
+ int minute, hour, day, month, weekday;
+ GregorianCalendar calendar = new GregorianCalendar();
+ calendar.setFirstDayOfWeek(GregorianCalendar.SUNDAY);
+ calendar.add(GregorianCalendar.MINUTE, 1);
+ calendar.set(GregorianCalendar.SECOND, 0);
+ calendar.setLenient(false);
+
+ // Weekday
+ for (;;) {
+ // Month
+ for (;;) {
+ // Day
+ for (;;) {
+ // Hour
+ for (;;) {
+ // Minute
+ for (;;) {
+ minute = getNextTimeSlice(minutesArray,
+ calendar.get(GregorianCalendar.MINUTE));
+ if (minute == -1) {
+ calendar.set(GregorianCalendar.MINUTE, 0);
+ calendar.add(GregorianCalendar.HOUR_OF_DAY, 1);
+ } else {
+ calendar.set(GregorianCalendar.MINUTE, minute);
+ break;
+ }
+ }
+ hour = getNextTimeSlice(hoursArray,
+ calendar.get(GregorianCalendar.HOUR_OF_DAY));
+ if (hour == -1) {
+ calendar.set(GregorianCalendar.HOUR_OF_DAY, 0);
+ calendar.add(GregorianCalendar.DAY_OF_MONTH, 1);
+ } else {
+ calendar.set(GregorianCalendar.HOUR_OF_DAY, hour);
+ break;
+ }
+ }
+ day = getNextTimeSlice(daysArray,
+ calendar.get(GregorianCalendar.DAY_OF_MONTH));
+ if (day == -1) {
+ calendar.set(GregorianCalendar.DAY_OF_MONTH, 1);
+ calendar.add(GregorianCalendar.MONTH, 1);
+ } else {
+ calendar.set(GregorianCalendar.DAY_OF_MONTH, day);
+ break;
+ }
+ }
+ month = getNextTimeSlice(monthArray,
+ (calendar.get(GregorianCalendar.MONTH) + 1));
+ if (month == -1) {
+ calendar.set(GregorianCalendar.MONTH, 0);
+ calendar.add(GregorianCalendar.YEAR, 1);
+ } else {
+ calendar.set(GregorianCalendar.MONTH, (month - 1));
+ break;
+ }
+ }
+ weekday = getNextTimeSlice(weekdayArray,
+ (calendar.get(GregorianCalendar.DAY_OF_WEEK) - 1));
+ if ((weekday == -1) ||
+ (weekday != (calendar.get(
+ GregorianCalendar.DAY_OF_WEEK) - 1)))
+ {
+ calendar.add(GregorianCalendar.DAY_OF_MONTH, 1);
+ } else {
+ break;
+ }
+ }
+
+ return calendar.getTime();
}
}
-
diff --git a/opends/src/server/org/opends/server/backends/task/Task.java b/opends/src/server/org/opends/server/backends/task/Task.java
index 82fc38f..d8e6ec3 100644
--- a/opends/src/server/org/opends/server/backends/task/Task.java
+++ b/opends/src/server/org/opends/server/backends/task/Task.java
@@ -580,6 +580,18 @@
}
/**
+ * Indicates whether or not this task is an iteration of
+ * some recurring task.
+ *
+ * @return boolean where true indicates that this task is
+ * recurring, false otherwise.
+ */
+ public boolean isRecurring()
+ {
+ return (recurringTaskID != null);
+ }
+
+ /**
* Indicates whether or not this task has been cancelled.
*
* @return boolean where true indicates that this task was
diff --git a/opends/src/server/org/opends/server/backends/task/TaskBackend.java b/opends/src/server/org/opends/server/backends/task/TaskBackend.java
index b158520..6029483 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskBackend.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -617,7 +617,11 @@
TaskState state = t.getTaskState();
if (TaskState.isPending(state))
{
- taskScheduler.removePendingTask(t.getTaskID());
+ if (t.isRecurring()) {
+ taskScheduler.removeRecurringTaskIteration(t.getTaskID());
+ } else {
+ taskScheduler.removePendingTask(t.getTaskID());
+ }
}
else if (TaskState.isDone(t.getTaskState()))
{
@@ -641,9 +645,6 @@
throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
}
-
- // Try to remove the recurring task. This will fail if there are any
- // associated iterations pending or running.
taskScheduler.removeRecurringTask(rt.getRecurringTaskID());
}
else
@@ -707,13 +708,12 @@
throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
}
-
// Look at the state of the task. We will allow anything to be altered
// for a pending task. For a running task, we will only allow the state
// to be altered in order to cancel it. We will not allow any
// modifications for completed tasks.
TaskState state = t.getTaskState();
- if (TaskState.isPending(state))
+ if (TaskState.isPending(state) && !t.isRecurring())
{
Task newTask = taskScheduler.entryToScheduledTask(newEntry,
modifyOperation);
@@ -727,50 +727,7 @@
// This will only be allowed using the replace modification type on
// the ds-task-state attribute if the value starts with "cancel" or
// "stop". In that case, we'll cancel the task.
- boolean acceptable = true;
- for (Modification m : modifyOperation.getModifications())
- {
- if (m.isInternal())
- {
- continue;
- }
-
- if (m.getModificationType() != ModificationType.REPLACE)
- {
- acceptable = false;
- break;
- }
-
- Attribute a = m.getAttribute();
- AttributeType at = a.getAttributeType();
- if (! at.hasName(ATTR_TASK_STATE))
- {
- acceptable = false;
- break;
- }
-
- Iterator<AttributeValue> iterator = a.iterator();
- if (! iterator.hasNext())
- {
- acceptable = false;
- break;
- }
-
- AttributeValue v = iterator.next();
- String valueString = toLowerCase(v.getStringValue());
- if (! (valueString.startsWith("cancel") ||
- valueString.startsWith("stop")))
- {
- acceptable = false;
- break;
- }
-
- if (iterator.hasNext())
- {
- acceptable = false;
- break;
- }
- }
+ boolean acceptable = isReplaceEntryAcceptable(modifyOperation);
if (acceptable)
{
@@ -786,6 +743,37 @@
message);
}
}
+ else if (TaskState.isPending(state) && t.isRecurring())
+ {
+ // Pending recurring task iterations can only be canceled.
+ boolean acceptable = isReplaceEntryAcceptable(modifyOperation);
+
+ if (acceptable)
+ {
+ Task newTask = taskScheduler.entryToScheduledTask(newEntry,
+ modifyOperation);
+ if (newTask.getTaskState() ==
+ TaskState.CANCELED_BEFORE_STARTING)
+ {
+ taskScheduler.removePendingTask(t.getTaskID());
+ taskScheduler.scheduleTask(newTask, true);
+ }
+ else if (newTask.getTaskState() ==
+ TaskState.STOPPED_BY_ADMINISTRATOR)
+ {
+ Message message = INFO_TASKBE_RUNNING_TASK_CANCELLED.get();
+ t.interruptTask(TaskState.STOPPED_BY_ADMINISTRATOR, message);
+ }
+ return;
+ }
+ else
+ {
+ Message message =
+ ERR_TASKBE_MODIFY_RECURRING.get(String.valueOf(entryDN));
+ throw new DirectoryException(
+ ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+ }
else
{
Message message =
@@ -820,6 +808,58 @@
/**
+ * Helper to determine if requested modifications are acceptable.
+ * @param modifyOperation associated with requested modifications.
+ * @return <CODE>true</CODE> if requested modifications are
+ * acceptable, <CODE>false</CODE> otherwise.
+ */
+ private boolean isReplaceEntryAcceptable(ModifyOperation modifyOperation)
+ {
+ boolean acceptable = true;
+
+ for (Modification m : modifyOperation.getModifications()) {
+ if (m.isInternal()) {
+ continue;
+ }
+
+ if (m.getModificationType() != ModificationType.REPLACE) {
+ acceptable = false;
+ break;
+ }
+
+ Attribute a = m.getAttribute();
+ AttributeType at = a.getAttributeType();
+ if (!at.hasName(ATTR_TASK_STATE)) {
+ acceptable = false;
+ break;
+ }
+
+ Iterator<AttributeValue> iterator = a.iterator();
+ if (!iterator.hasNext()) {
+ acceptable = false;
+ break;
+ }
+
+ AttributeValue v = iterator.next();
+ String valueString = toLowerCase(v.getStringValue());
+ if (!(valueString.startsWith("cancel") ||
+ valueString.startsWith("stop"))) {
+ acceptable = false;
+ break;
+ }
+
+ if (iterator.hasNext()) {
+ acceptable = false;
+ break;
+ }
+ }
+
+ return acceptable;
+ }
+
+
+
+ /**
* {@inheritDoc}
*/
@Override()
diff --git a/opends/src/server/org/opends/server/backends/task/TaskScheduler.java b/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
index 7dcd46b..6c30a95 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
@@ -37,6 +37,7 @@
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -55,6 +56,7 @@
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
+import org.opends.server.types.Attributes;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
@@ -191,6 +193,27 @@
DirectoryServer.registerAlertGenerator(this);
initializeTasksFromBackingFile();
+
+ for (RecurringTask recurringTask : recurringTasks.values()) {
+ Task task = null;
+ try {
+ task = recurringTask.scheduleNextIteration();
+ } catch (DirectoryException de) {
+ logError(de.getMessageObject());
+ }
+ if (task != null) {
+ try {
+ scheduleTask(task, false);
+ } catch (DirectoryException de) {
+ // This task might have been already scheduled from before
+ // and thus got initialized from backing file, otherwise
+ // log error and continue.
+ if (de.getResultCode() != ResultCode.ENTRY_ALREADY_EXISTS) {
+ logError(de.getMessageObject());
+ }
+ }
+ }
+ }
}
@@ -224,7 +247,12 @@
throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message);
}
- recurringTasks.put(id, recurringTask);
+ Attribute attr = Attributes.create(ATTR_TASK_STATE,
+ TaskState.RECURRING.toString());
+ ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+ attrList.add(attr);
+ Entry recurringTaskEntry = recurringTask.getRecurringTaskEntry();
+ recurringTaskEntry.putAttribute(attr.getAttributeType(), attrList);
if (scheduleIteration)
{
@@ -236,6 +264,7 @@
}
}
+ recurringTasks.put(id, recurringTask);
writeState();
}
finally
@@ -264,24 +293,19 @@
try
{
+ RecurringTask recurringTask = recurringTasks.remove(recurringTaskID);
+ writeState();
+
for (Task t : tasks.values())
{
if ((t.getRecurringTaskID() != null) &&
(t.getRecurringTaskID().equals(recurringTaskID)) &&
- (! TaskState.isDone(t.getTaskState())))
+ (!TaskState.isDone(t.getTaskState())))
{
- Message message = ERR_TASKSCHED_REMOVE_RECURRING_EXISTING_ITERATION.
- get(String.valueOf(recurringTaskID),
- String.valueOf(t.getTaskID()));
- throw new DirectoryException(
- ResultCode.UNWILLING_TO_PERFORM, message);
+ cancelTask(t.getTaskID());
}
}
-
- RecurringTask recurringTask = recurringTasks.remove(recurringTaskID);
- writeState();
-
return recurringTask;
}
finally
@@ -348,7 +372,15 @@
}
else if (TaskState.isDone(state))
{
- completedTasks.add(task);
+ if ((state == TaskState.CANCELED_BEFORE_STARTING) &&
+ task.isRecurring())
+ {
+ pendingTasks.add(task);
+ }
+ else
+ {
+ completedTasks.add(task);
+ }
}
else
{
@@ -459,6 +491,53 @@
/**
+ * Removes the specified pending iteration of recurring task. It will
+ * be removed from the task set but still be kept in the pending set.
+ *
+ * @param taskID The task ID of the pending iteration to remove.
+ *
+ * @return The task that was removed.
+ *
+ * @throws DirectoryException If the requested task is not in the
+ * pending queue.
+ */
+ public Task removeRecurringTaskIteration(String taskID)
+ throws DirectoryException
+ {
+ schedulerLock.lock();
+
+ try
+ {
+ Task t = tasks.get(taskID);
+ if (t == null)
+ {
+ Message message = ERR_TASKSCHED_REMOVE_PENDING_NO_SUCH_TASK.get(
+ String.valueOf(taskID));
+ throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
+ }
+
+ if (TaskState.isPending(t.getTaskState()))
+ {
+ tasks.remove(taskID);
+ writeState();
+ return t;
+ }
+ else
+ {
+ Message message = ERR_TASKSCHED_REMOVE_PENDING_NOT_PENDING.get(
+ String.valueOf(taskID));
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+ }
+ finally
+ {
+ schedulerLock.unlock();
+ }
+ }
+
+
+
+ /**
* Removes the specified completed task.
*
* @param taskID The task ID of the completed task to remove.
@@ -546,7 +625,12 @@
}
else
{
- Task newIteration = recurringTask.scheduleNextIteration();
+ Task newIteration = null;
+ try {
+ newIteration = recurringTask.scheduleNextIteration();
+ } catch (DirectoryException de) {
+ logError(de.getMessageObject());
+ }
if (newIteration != null)
{
// FIXME -- What to do if new iteration is null?
@@ -746,6 +830,7 @@
* Operates in a loop, launching tasks at the appropriate time and performing
* any necessary periodic cleanup.
*/
+ @Override
public void run()
{
isRunning = true;
@@ -797,6 +882,31 @@
long waitTime = t.getScheduledStartTime() - TimeThread.getTime();
sleepTime = Math.min(sleepTime, waitTime);
}
+ // Recurring task iteration has to spawn the next one
+ // even if the current iteration has been canceled.
+ else if ((state == TaskState.CANCELED_BEFORE_STARTING) &&
+ t.isRecurring())
+ {
+ if (t.getScheduledStartTime() > TimeThread.getTime()) {
+ // If we're waiting for the start time to arrive,
+ // then see if that will come before the next
+ // sleep time is up.
+ long waitTime =
+ t.getScheduledStartTime() - TimeThread.getTime();
+ sleepTime = Math.min(sleepTime, waitTime);
+ } else {
+ TaskThread taskThread;
+ if (idleThreads.isEmpty()) {
+ taskThread = new TaskThread(this, nextThreadID++);
+ taskThread.start();
+ } else {
+ taskThread = idleThreads.removeFirst();
+ }
+ runningTasks.add(t);
+ activeThreads.put(t.getTaskID(), taskThread);
+ taskThread.setTask(t);
+ }
+ }
if (state != t.getTaskState())
{
@@ -876,7 +986,13 @@
{
// If the task has finished we don't want to restart it
TaskState state = task.getTaskState();
- if (state != null && TaskState.isDone(state))
+
+ // Reset task state if recurring.
+ if (state == TaskState.RECURRING) {
+ state = null;
+ }
+
+ if ((state != null) && TaskState.isDone(state))
{
return state;
}
@@ -1011,26 +1127,6 @@
String.valueOf(taskBackend.getTaskRootDN()));
logError(message);
}
- else if (parentDN.equals(taskBackend.getRecurringTasksParentDN()))
- {
- try
- {
- RecurringTask recurringTask = entryToRecurringTask(entry);
- addRecurringTask(recurringTask, false);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- Message message =
- ERR_TASKSCHED_CANNOT_SCHEDULE_RECURRING_TASK_FROM_ENTRY.
- get(String.valueOf(entryDN), de.getMessageObject());
- logError(message);
- }
- }
else if (parentDN.equals(taskBackend.getScheduledTasksParentDN()))
{
try
@@ -1057,6 +1153,26 @@
logError(message);
}
}
+ else if (parentDN.equals(taskBackend.getRecurringTasksParentDN()))
+ {
+ try
+ {
+ RecurringTask recurringTask = entryToRecurringTask(entry);
+ addRecurringTask(recurringTask, false);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message message =
+ ERR_TASKSCHED_CANNOT_SCHEDULE_RECURRING_TASK_FROM_ENTRY.
+ get(String.valueOf(entryDN), de.getMessageObject());
+ logError(message);
+ }
+ }
else
{
Message message = ERR_TASKSCHED_INVALID_TASK_ENTRY_DN.get(
diff --git a/opends/src/server/org/opends/server/backends/task/TaskState.java b/opends/src/server/org/opends/server/backends/task/TaskState.java
index d3c10ba..be920e4 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskState.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskState.java
@@ -76,6 +76,13 @@
/**
+ * The task state that indicates that the task is recurring.
+ */
+ RECURRING(INFO_TASK_STATE_RECURRING.get()),
+
+
+
+ /**
* The task state that indicates that the task has completed without any
* errors.
*/
@@ -173,6 +180,27 @@
/**
+ * Indicates whether a task with the specified state is recurring.
+ *
+ * @param taskState The task state for which to make the determination.
+ *
+ * @return <CODE>true</CODE> if the task state indicates that the task
+ * is recurring, or <CODE>false</CODE> otherwise.
+ */
+ public static boolean isRecurring(TaskState taskState)
+ {
+ switch (taskState)
+ {
+ case RECURRING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+
+
+ /**
* Indicates whether a task with the specified state has completed all the
* processing that it will do, regardless of whether it completed its
* intended goal.
@@ -278,6 +306,10 @@
{
return RUNNING;
}
+ else if (lowerString.equals("recurring"))
+ {
+ return RECURRING;
+ }
else if (lowerString.equals("completed_successfully"))
{
return COMPLETED_SUCCESSFULLY;
diff --git a/opends/src/server/org/opends/server/backends/task/TaskThread.java b/opends/src/server/org/opends/server/backends/task/TaskThread.java
index 440bcc2..b9eb145 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskThread.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskThread.java
@@ -194,8 +194,11 @@
try
{
- TaskState returnState = getAssociatedTask().execute();
- getAssociatedTask().setTaskState(returnState);
+ if (!TaskState.isDone(getAssociatedTask().getTaskState()))
+ {
+ TaskState returnState = getAssociatedTask().execute();
+ getAssociatedTask().setTaskState(returnState);
+ }
}
catch (Exception e)
{
diff --git a/opends/src/server/org/opends/server/config/ConfigConstants.java b/opends/src/server/org/opends/server/config/ConfigConstants.java
index 96b52ee..8190143 100644
--- a/opends/src/server/org/opends/server/config/ConfigConstants.java
+++ b/opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -2109,11 +2109,11 @@
/**
- * The name of the configuration attribute that holds the name of the class
- * used to provide the implementation logic for a recurring task.
+ * The name of the configuration attribute that holds the
+ * schedule for a recurring task.
*/
- public static final String ATTR_RECURRING_TASK_CLASS_NAME =
- NAME_PREFIX_RECURRING_TASK + "class-name";
+ public static final String ATTR_RECURRING_TASK_SCHEDULE =
+ NAME_PREFIX_RECURRING_TASK + "schedule";
diff --git a/opends/src/server/org/opends/server/tasks/BackupTask.java b/opends/src/server/org/opends/server/tasks/BackupTask.java
index e611468..1547d06 100644
--- a/opends/src/server/org/opends/server/tasks/BackupTask.java
+++ b/opends/src/server/org/opends/server/tasks/BackupTask.java
@@ -147,6 +147,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public Message getDisplayName() {
return INFO_TASK_BACKUP_NAME.get();
}
@@ -154,6 +155,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public Message getAttributeDisplayName(String attrName) {
return argDisplayMap.get(attrName);
}
@@ -284,6 +286,12 @@
}
+ // Use task id for backup id in case of recurring task.
+ if (super.isRecurring()) {
+ backupID = super.getTaskID();
+ }
+
+
// If no backup ID was provided, then create one with the current timestamp.
if (backupID == null)
{
@@ -575,6 +583,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public void interruptTask(TaskState interruptState, Message interruptReason)
{
if (TaskState.STOPPED_BY_ADMINISTRATOR.equals(interruptState) &&
@@ -591,6 +600,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public boolean isInterruptable() {
return true;
}
diff --git a/opends/src/server/org/opends/server/tools/BackUpDB.java b/opends/src/server/org/opends/server/tools/BackUpDB.java
index d26e4d9..fa9c78b 100644
--- a/opends/src/server/org/opends/server/tools/BackUpDB.java
+++ b/opends/src/server/org/opends/server/tools/BackUpDB.java
@@ -1109,5 +1109,16 @@
}
return ret;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getTaskId() {
+ if (backupIDString != null) {
+ return backupIDString.getValue();
+ } else {
+ return null;
+ }
+ }
}
diff --git a/opends/src/server/org/opends/server/tools/ExportLDIF.java b/opends/src/server/org/opends/server/tools/ExportLDIF.java
index 9046833..bc6c706 100644
--- a/opends/src/server/org/opends/server/tools/ExportLDIF.java
+++ b/opends/src/server/org/opends/server/tools/ExportLDIF.java
@@ -1029,5 +1029,13 @@
return 1;
}
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getTaskId() {
+ // NYI.
+ return null;
+ }
}
diff --git a/opends/src/server/org/opends/server/tools/ImportLDIF.java b/opends/src/server/org/opends/server/tools/ImportLDIF.java
index 4a7e3a8..4d002fc 100644
--- a/opends/src/server/org/opends/server/tools/ImportLDIF.java
+++ b/opends/src/server/org/opends/server/tools/ImportLDIF.java
@@ -1414,5 +1414,13 @@
importConfig.close();
return retCode;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getTaskId() {
+ // NYI.
+ return null;
+ }
}
diff --git a/opends/src/server/org/opends/server/tools/ManageTasks.java b/opends/src/server/org/opends/server/tools/ManageTasks.java
index a8f6429..9a3a806 100644
--- a/opends/src/server/org/opends/server/tools/ManageTasks.java
+++ b/opends/src/server/org/opends/server/tools/ManageTasks.java
@@ -62,6 +62,7 @@
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import org.opends.server.backends.task.TaskState;
/**
* Tool for getting information and managing tasks in the Directory Server.
@@ -423,12 +424,11 @@
new TaskDrilldownMenu(taskId),
taskEntry.getType(), taskEntry.getState());
index++;
- if (taskEntry.isCancelable() && !taskEntry.isDone()) {
+ if (taskEntry.isCancelable()) {
cancelableIndices.add(index);
}
}
} else {
- // println();
getOutputStream().println(INFO_TASKINFO_NO_TASKS.get());
getOutputStream().println();
}
@@ -621,6 +621,7 @@
public MenuResult<TaskEntry> invoke(ManageTasks app)
throws CLIException
{
+ Message m = null;
TaskEntry taskEntry = null;
try {
taskEntry = app.getTaskClient().getTaskEntry(taskId);
@@ -640,23 +641,30 @@
table.appendCell(INFO_TASKINFO_FIELD_STATUS.get());
table.appendCell(taskEntry.getState());
- table.startRow();
- table.appendCell(INFO_TASKINFO_FIELD_SCHEDULED_START.get());
- Message m = taskEntry.getScheduledStartTime();
- if (m == null || m.equals(Message.EMPTY)) {
- table.appendCell(INFO_TASKINFO_IMMEDIATE_EXECUTION.get());
- } else {
+ if (TaskState.isRecurring(taskEntry.getTaskState())) {
+ table.startRow();
+ table.appendCell(INFO_TASKINFO_FIELD_SCHEDULED_START.get());
+ m = taskEntry.getScheduleTab();
table.appendCell(m);
+ } else {
+ table.startRow();
+ table.appendCell(INFO_TASKINFO_FIELD_SCHEDULED_START.get());
+ m = taskEntry.getScheduledStartTime();
+ if (m == null || m.equals(Message.EMPTY)) {
+ table.appendCell(INFO_TASKINFO_IMMEDIATE_EXECUTION.get());
+ } else {
+ table.appendCell(m);
+ }
+
+ table.startRow();
+ table.appendCell(INFO_TASKINFO_FIELD_ACTUAL_START.get());
+ table.appendCell(taskEntry.getActualStartTime());
+
+ table.startRow();
+ table.appendCell(INFO_TASKINFO_FIELD_COMPLETION_TIME.get());
+ table.appendCell(taskEntry.getCompletionTime());
}
- table.startRow();
- table.appendCell(INFO_TASKINFO_FIELD_ACTUAL_START.get());
- table.appendCell(taskEntry.getActualStartTime());
-
- table.startRow();
- table.appendCell(INFO_TASKINFO_FIELD_COMPLETION_TIME.get());
- table.appendCell(taskEntry.getCompletionTime());
-
writeMultiValueCells(
table,
INFO_TASKINFO_FIELD_DEPENDENCY.get(),
diff --git a/opends/src/server/org/opends/server/tools/RestoreDB.java b/opends/src/server/org/opends/server/tools/RestoreDB.java
index aad5a92..94f07ad 100644
--- a/opends/src/server/org/opends/server/tools/RestoreDB.java
+++ b/opends/src/server/org/opends/server/tools/RestoreDB.java
@@ -701,5 +701,16 @@
}
return 0;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getTaskId() {
+ if (backupIDString != null) {
+ return backupIDString.getValue();
+ } else {
+ return null;
+ }
+ }
}
diff --git a/opends/src/server/org/opends/server/tools/ToolConstants.java b/opends/src/server/org/opends/server/tools/ToolConstants.java
index 97432ba..553f3ce 100644
--- a/opends/src/server/org/opends/server/tools/ToolConstants.java
+++ b/opends/src/server/org/opends/server/tools/ToolConstants.java
@@ -576,6 +576,16 @@
public static final Character OPTION_SHORT_START_DATETIME = 't';
/**
+ * Recurring task option long form.
+ */
+ public static final String OPTION_LONG_RECURRING_TASK = "recurringTask";
+
+ /**
+ * Recurring task option short form.
+ */
+ public static final Character OPTION_SHORT_RECURRING_TASK = null;
+
+ /**
* The value for the long option propertiesFilePAth .
*/
public static final String OPTION_LONG_PROP_FILE_PATH = "propertiesFilePath";
diff --git a/opends/src/server/org/opends/server/tools/tasks/TaskClient.java b/opends/src/server/org/opends/server/tools/tasks/TaskClient.java
index 868bdce..8d58d55 100644
--- a/opends/src/server/org/opends/server/tools/tasks/TaskClient.java
+++ b/opends/src/server/org/opends/server/tools/tasks/TaskClient.java
@@ -69,7 +69,10 @@
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
+import org.opends.server.protocols.ldap.DeleteRequestProtocolOp;
+import org.opends.server.protocols.ldap.DeleteResponseProtocolOp;
/**
* Helper class for interacting with the task backend on behalf of utilities
@@ -109,16 +112,33 @@
public synchronized TaskEntry schedule(TaskScheduleInformation information)
throws LDAPException, IOException, ASN1Exception, TaskClientException
{
+ String taskID = null;
+ ASN1OctetString entryDN = null;
+ boolean scheduleRecurring = false;
+
LDAPReader reader = connection.getLDAPReader();
LDAPWriter writer = connection.getLDAPWriter();
- // Use a formatted time/date for the ID so that is remotely useful
- SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssMM");
- String taskID = df.format(new Date());
+ if (information.getRecurringDateTime() != null) {
+ scheduleRecurring = true;
+ }
- ASN1OctetString entryDN =
- new ASN1OctetString(ATTR_TASK_ID + "=" + taskID + "," +
- SCHEDULED_TASK_BASE_RDN + "," + DN_TASK_ROOT);
+ if (scheduleRecurring) {
+ taskID = information.getTaskId();
+ if ((taskID == null) || taskID.length() == 0) {
+ taskID = information.getTaskClass().getSimpleName() +
+ "-" + UUID.randomUUID().toString();
+ }
+ entryDN = new ASN1OctetString(ATTR_RECURRING_TASK_ID + "=" +
+ taskID + "," + RECURRING_TASK_BASE_RDN + "," + DN_TASK_ROOT);
+ } else {
+ // Use a formatted time/date for the ID so that is remotely useful
+ SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssMM");
+ taskID = df.format(new Date());
+
+ entryDN = new ASN1OctetString(ATTR_TASK_ID + "=" + taskID + "," +
+ SCHEDULED_TASK_BASE_RDN + "," + DN_TASK_ROOT);
+ }
ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
@@ -127,11 +147,20 @@
ArrayList<ASN1OctetString> ocValues = new ArrayList<ASN1OctetString>(3);
ocValues.add(new ASN1OctetString("top"));
ocValues.add(new ASN1OctetString(ConfigConstants.OC_TASK));
+
+ if (scheduleRecurring) {
+ ocValues.add(new ASN1OctetString(ConfigConstants.OC_RECURRING_TASK));
+ }
+
ocValues.add(new ASN1OctetString(information.getTaskObjectclass()));
attributes.add(new LDAPAttribute(ATTR_OBJECTCLASS, ocValues));
ArrayList<ASN1OctetString> taskIDValues = new ArrayList<ASN1OctetString>(1);
taskIDValues.add(new ASN1OctetString(taskID));
+
+ if (scheduleRecurring) {
+ attributes.add(new LDAPAttribute(ATTR_RECURRING_TASK_ID, taskIDValues));
+ }
attributes.add(new LDAPAttribute(ATTR_TASK_ID, taskIDValues));
ArrayList<ASN1OctetString> classValues = new ArrayList<ASN1OctetString>(1);
@@ -149,6 +178,15 @@
startDateValues));
}
+ if (scheduleRecurring) {
+ ArrayList<ASN1OctetString> recurringPatternValues =
+ new ArrayList<ASN1OctetString>(1);
+ recurringPatternValues.add(new ASN1OctetString(
+ information.getRecurringDateTime()));
+ attributes.add(new LDAPAttribute(ATTR_RECURRING_TASK_SCHEDULE,
+ recurringPatternValues));
+ }
+
// add dependency IDs
List<String> dependencyIds = information.getDependencyIds();
if (dependencyIds != null && dependencyIds.size() > 0) {
@@ -340,14 +378,13 @@
* Changes that the state of the task in the backend to a canceled state.
*
* @param id if the task to cancel
- * @return Entry of the task before the modification
* @throws IOException if there is a stream communication problem
* @throws LDAPException if there is a problem getting information
* out to the directory
* @throws ASN1Exception if there is a problem with the encoding
* @throws TaskClientException if there is no task with the requested id
*/
- public synchronized TaskEntry cancelTask(String id)
+ public synchronized void cancelTask(String id)
throws TaskClientException, IOException, ASN1Exception, LDAPException
{
LDAPReader reader = connection.getLDAPReader();
@@ -373,11 +410,6 @@
LDAPAttribute attr = new LDAPAttribute(ATTR_TASK_STATE, values);
mods.add(new LDAPModification(ModificationType.REPLACE, attr));
- // We have to reset the start time or the scheduler will
- // reschedule to task.
- // attr = new LDAPAttribute(ATTR_TASK_SCHEDULED_START_TIME);
- // mods.add(new LDAPModification(ModificationType.DELETE, attr));
-
ModifyRequestProtocolOp modRequest =
new ModifyRequestProtocolOp(dn, mods);
LDAPMessage requestMessage =
@@ -409,6 +441,41 @@
LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR,
errorMessage);
}
+ } else if (TaskState.isRecurring(state)) {
+
+ ASN1OctetString dn = new ASN1OctetString(entry.getDN().toString());
+ DeleteRequestProtocolOp deleteRequest =
+ new DeleteRequestProtocolOp(dn);
+
+ LDAPMessage requestMessage = new LDAPMessage(
+ nextMessageID.getAndIncrement(), deleteRequest, null);
+
+ writer.writeMessage(requestMessage);
+
+ LDAPMessage responseMessage = reader.readMessage();
+
+ if (responseMessage == null) {
+ Message message = ERR_TASK_CLIENT_UNEXPECTED_CONNECTION_CLOSURE.get();
+ throw new LDAPException(UNAVAILABLE.getIntValue(), message);
+ }
+
+ if (responseMessage.getProtocolOpType() !=
+ LDAPConstants.OP_TYPE_DELETE_RESPONSE)
+ {
+ throw new LDAPException(
+ LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR,
+ ERR_TASK_CLIENT_INVALID_RESPONSE_TYPE.get(
+ responseMessage.getProtocolOpName()));
+ }
+
+ DeleteResponseProtocolOp deleteResponse =
+ responseMessage.getDeleteResponseProtocolOp();
+ Message errorMessage = deleteResponse.getErrorMessage();
+ if (errorMessage != null) {
+ throw new LDAPException(
+ LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR,
+ errorMessage);
+ }
} else {
throw new TaskClientException(
ERR_TASK_CLIENT_UNCANCELABLE_TASK.get(id));
@@ -417,7 +484,6 @@
throw new TaskClientException(
ERR_TASK_CLIENT_TASK_STATE_UNKNOWN.get(id));
}
- return getTaskEntry(id);
}
diff --git a/opends/src/server/org/opends/server/tools/tasks/TaskEntry.java b/opends/src/server/org/opends/server/tools/tasks/TaskEntry.java
index e84e048..30448f7 100644
--- a/opends/src/server/org/opends/server/tools/tasks/TaskEntry.java
+++ b/opends/src/server/org/opends/server/tools/tasks/TaskEntry.java
@@ -82,6 +82,8 @@
supAttrNames.add("ds-task-log-message");
supAttrNames.add("ds-task-notify-on-completion");
supAttrNames.add("ds-task-notify-on-error");
+ supAttrNames.add("ds-recurring-task-id");
+ supAttrNames.add("ds-recurring-task-schedule");
}
private String id;
@@ -90,6 +92,7 @@
private String schedStart;
private String actStart;
private String compTime;
+ private String schedTab;
private List<String> depends;
private String depFailAct;
private List<String> logs;
@@ -126,6 +129,7 @@
logs = getMultiStringValue(entry, p + "log-message");
notifyErr = getMultiStringValue(entry, p + "notify-on-error");
notifyComp = getMultiStringValue(entry, p + "notify-on-completion");
+ schedTab = getSingleStringValue(entry, "ds-recurring-task-schedule");
// Build a map of non-superior attribute value pairs for display
@@ -223,6 +227,15 @@
}
/**
+ * Gets recurring schedule tab.
+ *
+ * @return Message tab string
+ */
+ public Message getScheduleTab() {
+ return Message.raw(schedTab);
+ }
+
+ /**
* Gets the IDs of tasks upon which this task depends.
*
* @return array of IDs
@@ -326,6 +339,7 @@
if (state != null) {
Task task = getTask();
cancelable = (TaskState.isPending(state) ||
+ TaskState.isRecurring(state) ||
(TaskState.isRunning(state) &&
task != null &&
task.isInterruptable()));
diff --git a/opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java b/opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java
index 87b4081..bd5ad99 100644
--- a/opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java
+++ b/opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java
@@ -77,6 +77,23 @@
/**
+ * Gets an arbitrary task id assigned to this task.
+ *
+ * @return assigned task id if any or <CODE>null</CODE> otherwise.
+ */
+ String getTaskId();
+
+
+ /**
+ * Gets the date/time pattern for recurring task schedule.
+ *
+ * @return recurring date/time pattern at which the task
+ * should be scheduled.
+ */
+ String getRecurringDateTime();
+
+
+ /**
* Gets a list of task IDs upon which this task is dependent.
*
* @return list of task IDs
diff --git a/opends/src/server/org/opends/server/tools/tasks/TaskTool.java b/opends/src/server/org/opends/server/tools/tasks/TaskTool.java
index 0b77a00..344f22e 100644
--- a/opends/src/server/org/opends/server/tools/tasks/TaskTool.java
+++ b/opends/src/server/org/opends/server/tools/tasks/TaskTool.java
@@ -88,6 +88,9 @@
// Argument for describing the task's start time
StringArgument startArg;
+ // Argument to indicate a recurring task
+ StringArgument recurringArg;
+
// Argument for specifying completion notifications
StringArgument completionNotificationArg;
@@ -133,79 +136,88 @@
* @return LDAPConnectionArgumentParser for processing CLI input
*/
protected LDAPConnectionArgumentParser createArgParser(String className,
- Message toolDescription)
- {
+ Message toolDescription)
+ {
ArgumentGroup ldapGroup = new ArgumentGroup(
- INFO_DESCRIPTION_TASK_LDAP_ARGS.get(), 1001);
+ INFO_DESCRIPTION_TASK_LDAP_ARGS.get(), 1001);
argParser = new LDAPConnectionArgumentParser(className,
- toolDescription, false, ldapGroup, alwaysSSL);
+ toolDescription, false, ldapGroup, alwaysSSL);
ArgumentGroup taskGroup = new ArgumentGroup(
- INFO_DESCRIPTION_TASK_TASK_ARGS.get(), 1000);
+ INFO_DESCRIPTION_TASK_TASK_ARGS.get(), 1000);
try {
StringArgument propertiesFileArgument = new StringArgument(
- "propertiesFilePath",
- null, OPTION_LONG_PROP_FILE_PATH,
- false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null,
- INFO_DESCRIPTION_PROP_FILE_PATH.get());
+ "propertiesFilePath",
+ null, OPTION_LONG_PROP_FILE_PATH,
+ false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null,
+ INFO_DESCRIPTION_PROP_FILE_PATH.get());
argParser.addArgument(propertiesFileArgument);
argParser.setFilePropertiesArgument(propertiesFileArgument);
- BooleanArgument noPropertiesFileArgument = new BooleanArgument(
- "noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE,
- INFO_DESCRIPTION_NO_PROP_FILE.get());
- argParser.addArgument(noPropertiesFileArgument);
- argParser.setNoPropertiesFileArgument(noPropertiesFileArgument);
+ BooleanArgument noPropertiesFileArgument = new BooleanArgument(
+ "noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE,
+ INFO_DESCRIPTION_NO_PROP_FILE.get());
+ argParser.addArgument(noPropertiesFileArgument);
+ argParser.setNoPropertiesFileArgument(noPropertiesFileArgument);
startArg = new StringArgument(
- OPTION_LONG_START_DATETIME,
- OPTION_SHORT_START_DATETIME,
- OPTION_LONG_START_DATETIME, false, false,
- true, INFO_START_DATETIME_PLACEHOLDER.get(),
- null, null,
- INFO_DESCRIPTION_START_DATETIME.get());
+ OPTION_LONG_START_DATETIME,
+ OPTION_SHORT_START_DATETIME,
+ OPTION_LONG_START_DATETIME, false, false,
+ true, INFO_START_DATETIME_PLACEHOLDER.get(),
+ null, null,
+ INFO_DESCRIPTION_START_DATETIME.get());
argParser.addArgument(startArg, taskGroup);
+ recurringArg = new StringArgument(
+ OPTION_LONG_RECURRING_TASK,
+ OPTION_SHORT_RECURRING_TASK,
+ OPTION_LONG_RECURRING_TASK, false, false,
+ true, INFO_RECURRING_TASK_PLACEHOLDER.get(),
+ null, null,
+ INFO_DESCRIPTION_RECURRING_TASK.get());
+ argParser.addArgument(recurringArg, taskGroup);
+
completionNotificationArg = new StringArgument(
- OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL,
- OPTION_SHORT_COMPLETION_NOTIFICATION_EMAIL,
- OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL,
- false, true, true, INFO_EMAIL_ADDRESS_PLACEHOLDER.get(),
- null, null, INFO_DESCRIPTION_TASK_COMPLETION_NOTIFICATION.get());
+ OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL,
+ OPTION_SHORT_COMPLETION_NOTIFICATION_EMAIL,
+ OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL,
+ false, true, true, INFO_EMAIL_ADDRESS_PLACEHOLDER.get(),
+ null, null, INFO_DESCRIPTION_TASK_COMPLETION_NOTIFICATION.get());
argParser.addArgument(completionNotificationArg, taskGroup);
errorNotificationArg = new StringArgument(
- OPTION_LONG_ERROR_NOTIFICATION_EMAIL,
- OPTION_SHORT_ERROR_NOTIFICATION_EMAIL,
- OPTION_LONG_ERROR_NOTIFICATION_EMAIL,
- false, true, true, INFO_EMAIL_ADDRESS_PLACEHOLDER.get(),
- null, null, INFO_DESCRIPTION_TASK_ERROR_NOTIFICATION.get());
+ OPTION_LONG_ERROR_NOTIFICATION_EMAIL,
+ OPTION_SHORT_ERROR_NOTIFICATION_EMAIL,
+ OPTION_LONG_ERROR_NOTIFICATION_EMAIL,
+ false, true, true, INFO_EMAIL_ADDRESS_PLACEHOLDER.get(),
+ null, null, INFO_DESCRIPTION_TASK_ERROR_NOTIFICATION.get());
argParser.addArgument(errorNotificationArg, taskGroup);
dependencyArg = new StringArgument(
- OPTION_LONG_DEPENDENCY,
- OPTION_SHORT_DEPENDENCY,
- OPTION_LONG_DEPENDENCY,
- false, true, true, INFO_TASK_ID_PLACEHOLDER.get(),
- null, null, INFO_DESCRIPTION_TASK_DEPENDENCY_ID.get());
+ OPTION_LONG_DEPENDENCY,
+ OPTION_SHORT_DEPENDENCY,
+ OPTION_LONG_DEPENDENCY,
+ false, true, true, INFO_TASK_ID_PLACEHOLDER.get(),
+ null, null, INFO_DESCRIPTION_TASK_DEPENDENCY_ID.get());
argParser.addArgument(dependencyArg, taskGroup);
Set fdaValSet = EnumSet.allOf(FailedDependencyAction.class);
failedDependencyActionArg = new StringArgument(
- OPTION_LONG_FAILED_DEPENDENCY_ACTION,
- OPTION_SHORT_FAILED_DEPENDENCY_ACTION,
- OPTION_LONG_FAILED_DEPENDENCY_ACTION,
- false, true, true, INFO_ACTION_PLACEHOLDER.get(),
- null, null, INFO_DESCRIPTION_TASK_FAILED_DEPENDENCY_ACTION.get(
- StaticUtils.collectionToString(fdaValSet, ","),
- FailedDependencyAction.defaultValue().name()));
+ OPTION_LONG_FAILED_DEPENDENCY_ACTION,
+ OPTION_SHORT_FAILED_DEPENDENCY_ACTION,
+ OPTION_LONG_FAILED_DEPENDENCY_ACTION,
+ false, true, true, INFO_ACTION_PLACEHOLDER.get(),
+ null, null, INFO_DESCRIPTION_TASK_FAILED_DEPENDENCY_ACTION.get(
+ StaticUtils.collectionToString(fdaValSet, ","),
+ FailedDependencyAction.defaultValue().name()));
argParser.addArgument(failedDependencyActionArg, taskGroup);
testIfOfflineArg = new BooleanArgument(
- "testIfOffline", null, "testIfOffline",
- INFO_DESCRIPTION_TEST_IF_OFFLINE.get());
+ "testIfOffline", null, "testIfOffline",
+ INFO_DESCRIPTION_TEST_IF_OFFLINE.get());
testIfOfflineArg.setHidden(true);
argParser.addArgument(testIfOfflineArg);
@@ -311,6 +323,19 @@
/**
* {@inheritDoc}
*/
+ public String getRecurringDateTime() {
+ String pattern = null;
+
+ // If the recurring task arg is present parse its value
+ if (recurringArg != null && recurringArg.isPresent()) {
+ pattern = recurringArg.getValue();
+ }
+ return pattern;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public List<String> getDependencyIds() {
if (dependencyArg.isPresent()) {
return dependencyArg.getValues();
@@ -405,13 +430,18 @@
TaskClient tc = new TaskClient(conn);
TaskEntry taskEntry = tc.schedule(this);
Message startTime = taskEntry.getScheduledStartTime();
- if (startTime == null || startTime.length() == 0) {
+ if (taskEntry.getTaskState() == TaskState.RECURRING) {
+ out.println(
+ wrapText(INFO_TASK_TOOL_RECURRING_TASK_SCHEDULED.get(
+ taskEntry.getType(),
+ taskEntry.getId()),
+ MAX_LINE_WIDTH));
+ } else if (startTime == null || startTime.length() == 0) {
out.println(
wrapText(INFO_TASK_TOOL_TASK_SCHEDULED_NOW.get(
taskEntry.getType(),
taskEntry.getId()),
MAX_LINE_WIDTH));
-
} else {
out.println(
wrapText(INFO_TASK_TOOL_TASK_SCHEDULED_FUTURE.get(
@@ -443,12 +473,13 @@
} while (!taskEntry.isDone());
if (TaskState.isSuccessful(taskEntry.getTaskState())) {
- out.println(
- wrapText(INFO_TASK_TOOL_TASK_SUCESSFULL.get(
- taskEntry.getType(),
- taskEntry.getId()),
- MAX_LINE_WIDTH));
-
+ if (taskEntry.getTaskState() != TaskState.RECURRING) {
+ out.println(
+ wrapText(INFO_TASK_TOOL_TASK_SUCESSFULL.get(
+ taskEntry.getType(),
+ taskEntry.getId()),
+ MAX_LINE_WIDTH));
+ }
return 0;
} else {
out.println(
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
index 6e4e313..09bb550 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -168,7 +168,7 @@
* cases that depend on this specific value of "o=test".
*/
public static final String TEST_ROOT_DN_STRING = "o=test";
-
+
/**
* The backend if for the test backend
*/
@@ -178,7 +178,7 @@
* The string representation of the OpenDMK jar file location
* that will be used as base to determine if snmp is included or not
*/
- public static final String PROPERTY_OPENDMK_LOCATION =
+ public static final String PROPERTY_OPENDMK_LOCATION =
"org.opends.server.snmp.opendmk";
/**
@@ -304,7 +304,7 @@
testInstallRoot.mkdirs();
testInstanceRoot.mkdirs();
}
-
+
File testInstanceSchema =
new File (testInstanceRoot, "config" + File.separator + "schema");
testInstanceSchema.mkdirs();
@@ -341,34 +341,34 @@
File testClassesDir = new File(testInstanceRoot, "classes");
File testLibDir = new File(testInstallRoot, "lib");
File testBinDir = new File(testInstallRoot, "bin");
-
+
// Snmp resource
- String opendmkJarFileLocation =
+ String opendmkJarFileLocation =
System.getProperty(PROPERTY_OPENDMK_LOCATION);
-
+
File opendmkJar = new File(opendmkJarFileLocation, "jdmkrt.jar");
-
+
File snmpResourceDir = new File(buildRoot + File.separator + "src" +
File.separator + "snmp" + File.separator +
"resource");
-
+
File snmpConfigDir = new File(snmpResourceDir, "config");
-
+
File testSnmpResourceDir = new File (testConfigDir + File.separator +
"snmp");
-
+
if (Boolean.getBoolean(PROPERTY_COPY_CLASSES_TO_TEST_PKG))
{
copyDirectory(serverClassesDir, testClassesDir);
copyDirectory(unitClassesDir, testClassesDir);
}
-
+
if (installedRoot != null)
{
copyDirectory(new File(installedRoot), testInstallRoot);
-
+
// Get the instance location
-
+
}
else
{
@@ -508,7 +508,7 @@
DirectoryEnvironmentConfig config = new DirectoryEnvironmentConfig();
config.setServerRoot(testInstallRoot);
config.setInstanceRoot(testInstanceRoot);
-
+
config.setForceDaemonThreads(true);
config.setConfigClass(ConfigFileHandler.class);
config.setConfigFile(new File(testConfigDir, "config.ldif"));
@@ -648,7 +648,7 @@
if (testConfigDir == null) {
throw new RuntimeException("The testConfigDir variable is not set yet!");
}
-
+
return (testConfigDir);
}
@@ -974,7 +974,7 @@
in.close();
out.close();
}
-
+
public static void appendFile(File src, File dst) throws IOException {
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst, true);
@@ -988,7 +988,7 @@
in.close();
out.close();
}
-
+
/**
* Get the LDAP port the test environment Directory Server instance is
@@ -1305,6 +1305,33 @@
/**
+ * Adds the provided entry to the Directory Server using an internal
+ * operation.
+ *
+ * @param lines The lines that make up the entry to be added.
+ *
+ * @return result code for this operation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ public static ResultCode addEntryOperation(String... lines)
+ throws Exception
+ {
+ Entry entry = makeEntry(lines);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+
+ AddOperation addOperation = conn.processAdd(entry.getDN(),
+ entry.getObjectClasses(),
+ entry.getUserAttributes(),
+ entry.getOperationalAttributes());
+ return addOperation.getResultCode();
+ }
+
+
+
+ /**
* Adds the provided set of entries to the Directory Server using internal
* operations.
*
@@ -1387,7 +1414,7 @@
"-a",
"-f", path
};
-
+
if (useAdminPort) {
return LDAPModify.mainModify(adminArgs, false, null, null);
} else {
@@ -1749,7 +1776,7 @@
} catch (Exception e) {
hostName="Unknown (" + e + ")";
}
-
+
String[] fullArgs = new String[args.length + 11];
fullArgs[0] = "-h";
fullArgs[1] = hostName;
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/task/TaskBackendTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/task/TaskBackendTestCase.java
index 0293b9a..beafe6b 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/task/TaskBackendTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/task/TaskBackendTestCase.java
@@ -30,8 +30,10 @@
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.GregorianCalendar;
import java.util.TimeZone;
+import java.util.UUID;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -42,10 +44,10 @@
import org.opends.server.tasks.TasksTestCase;
import org.opends.server.types.DN;
+import org.opends.server.types.ResultCode;
import static org.testng.Assert.*;
import static org.opends.server.util.ServerConstants.*;
-import static org.opends.server.util.StaticUtils.*;
@@ -405,5 +407,130 @@
assertEquals(resultCode, 0);
assertFalse(DirectoryServer.entryExists(DN.decode(taskDN)));
}
-}
+
+
+ /**
+ * Tests basic recurring task functionality and parser.
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testRecurringTask()
+ throws Exception
+ {
+ String taskID = "testRecurringTask";
+ String taskDN = "ds-recurring-task-id=" +
+ taskID + ",cn=Recurring Tasks,cn=tasks";
+ String taskSchedule = "00 * * * *";
+
+ String[] invalidTaskSchedules = {
+ "* * * *", "* * * * * *", "*:*:*:*:*",
+ "60 * * * *", "-1 * * * *", "1-60 * * * *", "1,60 * * * *",
+ "* 24 * * *", "* -1 * * *", "* 1-24 * * *", "* 1,24 * * *",
+ "* * 32 * *", "* * 0 * *", "* * 1-32 * *", "* * 1,32 * *",
+ "* * * 13 *", "* * * 0 *", "* * * 1-13 *", "* * * 1,13 *",
+ "* * * * 7", "* * * * -1", "* * * * 1-7", "* * * * 1,7",
+ "* * 31 2 *" };
+ String[] validTaskSchedules = {
+ "* * * * *",
+ "59 * * * *", "0 * * * *", "0-59 * * * *", "0,59 * * * *",
+ "* 23 * * *", "* 0 * * *", "* 0-23 * * *", "* 0,23 * * *",
+ "* * 31 * *", "* * 1 * *", "* * 1-31 * *", "* * 1,31 * *",
+ "* * * 12 *", "* * * 1 *", "* * * 1-12 *", "* * * 1,12 *",
+ "* * * * 6", "* * * * 0", "* * * * 0-6", "* * * * 0,6" };
+
+ GregorianCalendar calendar = new GregorianCalendar();
+ calendar.setFirstDayOfWeek(GregorianCalendar.SUNDAY);
+ calendar.setLenient(false);
+ calendar.add(GregorianCalendar.HOUR_OF_DAY, 1);
+ calendar.set(GregorianCalendar.MINUTE, 0);
+ calendar.set(GregorianCalendar.SECOND, 0);
+
+ Date scheduledDate = calendar.getTime();
+ String scheduledTaskID = taskID + " - " + scheduledDate.toString();
+ String scheduledTaskDN = "ds-task-id=" + scheduledTaskID +
+ ",cn=Scheduled Tasks,cn=tasks";
+
+ assertTrue(addRecurringTask(taskID, taskSchedule));
+
+ Task scheduledTask = TasksTestCase.getTask(DN.decode(scheduledTaskDN));
+ assertTrue(TaskState.isPending(scheduledTask.getTaskState()));
+
+ // Perform a modification to update a non-state attribute.
+ int resultCode = TestCaseUtils.applyModifications(true,
+ "dn: " + taskDN,
+ "changetype: modify",
+ "replace: ds-recurring-task-schedule",
+ "ds-recurring-task-schedule: * * * * *");
+ assertFalse(resultCode == 0);
+
+ // Delete recurring task.
+ resultCode = TestCaseUtils.applyModifications(true,
+ "dn: " + taskDN,
+ "changetype: delete");
+ assertEquals(resultCode, 0);
+ assertFalse(DirectoryServer.entryExists(DN.decode(taskDN)));
+
+ // Make sure scheduled task got canceled.
+ scheduledTask = TasksTestCase.getTask(DN.decode(scheduledTaskDN));
+ assertTrue(TaskState.isCancelled(scheduledTask.getTaskState()));
+
+ // Test parser with invalid schedules.
+ for (String invalidSchedule : invalidTaskSchedules) {
+ assertFalse(addRecurringTask(taskID, invalidSchedule));
+ }
+
+ // Test parser with valid schedules.
+ for (String validSchedule : validTaskSchedules) {
+ taskID = "testRecurringTask" + "-" + UUID.randomUUID();
+ taskDN = "ds-recurring-task-id=" + taskID +
+ ",cn=Recurring Tasks,cn=tasks";
+ assertTrue(addRecurringTask(taskID, validSchedule));
+ // Delete recurring task.
+ resultCode = TestCaseUtils.applyModifications(true,
+ "dn: " + taskDN,
+ "changetype: delete");
+ assertEquals(resultCode, 0);
+ assertFalse(DirectoryServer.entryExists(DN.decode(taskDN)));
+ }
+ }
+
+
+
+ /**
+ * Adds recurring task to the task backend.
+ *
+ * @param taskID recurring task id.
+ *
+ * @param taskSchedule recurring task schedule.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ *
+ * @return <CODE>true</CODE> if task successfully added to
+ * the task backend, <CODE>false</CODE> otherwise.
+ */
+ @Test(enabled=false) // This isn't a test method, but TestNG thinks it is.
+ private boolean addRecurringTask(String taskID, String taskSchedule)
+ throws Exception
+ {
+ String taskDN = "ds-recurring-task-id=" +
+ taskID + ",cn=Recurring Tasks,cn=tasks";
+
+ ResultCode rc = TestCaseUtils.addEntryOperation(
+ "dn: " + taskDN,
+ "objectClass: top",
+ "objectClass: ds-task",
+ "objectClass: ds-recurring-task",
+ "objectClass: extensibleObject",
+ "ds-recurring-task-id: " + taskID,
+ "ds-recurring-task-schedule: " + taskSchedule,
+ "ds-task-id: " + taskID,
+ "ds-task-class-name: org.opends.server.tasks.DummyTask",
+ "ds-task-dummy-sleep-time: 0");
+
+ if (rc != ResultCode.SUCCESS) {
+ return false;
+ }
+ return DirectoryServer.entryExists(DN.decode(taskDN));
+ }
+}
--
Gitblit v1.10.0