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/src/server/org/opends/server/backends/task/RecurringTask.java | 406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 381 insertions(+), 25 deletions(-)
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();
}
}
-
--
Gitblit v1.10.0