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' 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 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 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 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.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(); @@ -309,11 +388,288 @@ * Schedules the next iteration of this recurring task 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(); } } 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 opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -617,8 +617,12 @@ TaskState state = t.getTaskState(); if (TaskState.isPending(state)) { if (t.isRecurring()) { taskScheduler.removeRecurringTaskIteration(t.getTaskID()); } else { taskScheduler.removePendingTask(t.getTaskID()); } } else if (TaskState.isDone(t.getTaskState())) { taskScheduler.removeCompletedTask(t.getTaskID()); @@ -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() 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()))) { 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,8 +372,16 @@ } else if (TaskState.isDone(state)) { if ((state == TaskState.CANCELED_BEFORE_STARTING) && task.isRecurring()) { pendingTasks.add(task); } else { completedTasks.add(task); } } else { pendingTasks.add(task); @@ -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( 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; opends/src/server/org/opends/server/backends/task/TaskThread.java
@@ -194,9 +194,12 @@ try { if (!TaskState.isDone(getAssociatedTask().getTaskState())) { TaskState returnState = getAssociatedTask().execute(); getAssociatedTask().setTaskState(returnState); } } catch (Exception e) { if (debugEnabled()) 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"; 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; } 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; } } } opends/src/server/org/opends/server/tools/ExportLDIF.java
@@ -1029,5 +1029,13 @@ return 1; } } /** * {@inheritDoc} */ public String getTaskId() { // NYI. return null; } } opends/src/server/org/opends/server/tools/ImportLDIF.java
@@ -1414,5 +1414,13 @@ importConfig.close(); return retCode; } /** * {@inheritDoc} */ public String getTaskId() { // NYI. return null; } } 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,9 +641,15 @@ table.appendCell(INFO_TASKINFO_FIELD_STATUS.get()); table.appendCell(taskEntry.getState()); if (TaskState.isRecurring(taskEntry.getTaskState())) { table.startRow(); table.appendCell(INFO_TASKINFO_FIELD_SCHEDULED_START.get()); Message m = taskEntry.getScheduledStartTime(); 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 { @@ -656,6 +663,7 @@ table.startRow(); table.appendCell(INFO_TASKINFO_FIELD_COMPLETION_TIME.get()); table.appendCell(taskEntry.getCompletionTime()); } writeMultiValueCells( table, 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; } } } 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"; 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(); if (information.getRecurringDateTime() != null) { scheduleRecurring = true; } 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"); String taskID = df.format(new Date()); taskID = df.format(new Date()); ASN1OctetString entryDN = new ASN1OctetString(ATTR_TASK_ID + "=" + taskID + "," + 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); } 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())); 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 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; @@ -168,6 +171,15 @@ 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, @@ -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())) { 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( opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -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. * 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)); } }