From 20f73bd1f2eac1aeccfeea9da83294f58ecd723a Mon Sep 17 00:00:00 2001
From: Chris Ridd <chris.ridd@forgerock.com>
Date: Wed, 03 Dec 2014 14:53:53 +0000
Subject: [PATCH] Backport fix OPENDJ-1614 (CR-5290) Improve crontab(5) support in recurring tasks
---
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/task/TaskBackendTestCase.java | 102 +++++++++++---------
opends/src/messages/messages/admin_tool.properties | 6
opends/src/server/org/opends/server/backends/task/RecurringTask.java | 171 ++++++++++++++++++---------------
3 files changed, 153 insertions(+), 126 deletions(-)
diff --git a/opends/src/messages/messages/admin_tool.properties b/opends/src/messages/messages/admin_tool.properties
index 545da64..da2f031 100644
--- a/opends/src/messages/messages/admin_tool.properties
+++ b/opends/src/messages/messages/admin_tool.properties
@@ -2825,9 +2825,9 @@
#
# Note that the following property contains line breaks in HTML format (<br>).
#
-INFO_CTRL_PANEL_CRON_HELP=Use ',' to separate values. For example: \
- '1,4,5'.<br>Use '-' to indicate intervals. For example '1-5'.<br>Use '*' to \
- indicate any value.
+INFO_CTRL_PANEL_CRON_HELP=Use ',' to separate values and intervals. For example: '1-3,5'.<br>\
+ Use '-' to indicate intervals. Append '/' and a number to skip through the interval. For example '1-5/2'.<br>\
+ Use '*' to indicate any value. Append '/' and a number to skip through the values. For example '*/2'.
SEVERE_ERR_CTRL_PANEL_INVALID_HOUR=The provided hour value is not valid.
SEVERE_ERR_CTRL_PANEL_INVALID_MINUTE=The provided minute value is not valid.
SEVERE_ERR_CTRL_PANEL_INVALID_DAY=The provided day value is not valid.
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 2b91d0f..f37ad8c 100644
--- a/opends/src/server/org/opends/server/backends/task/RecurringTask.java
+++ b/opends/src/server/org/opends/server/backends/task/RecurringTask.java
@@ -22,21 +22,20 @@
*
*
* Copyright 2006-2010 Sun Microsystems, Inc.
+ * Portions Copyright 2014 ForgeRock, AS
*/
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;
-
-
-
import java.util.Iterator;
import java.util.List;
-
import java.util.StringTokenizer;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
@@ -58,8 +57,6 @@
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.ServerConstants.*;
-
-
/**
* This class defines a information about a recurring task, which will be used
* to repeatedly schedule tasks for processing.
@@ -74,63 +71,57 @@
*/
private static final DebugTracer TRACER = getTracer();
-
-
-
- // The DN of the entry that actually defines this task.
+ /** The DN of the entry that actually defines this task. */
private final DN recurringTaskEntryDN;
- // The entry that actually defines this task.
+ /** The entry that actually defines this task. */
private final Entry recurringTaskEntry;
- // The unique ID for this recurring task.
+ /** The unique ID for this recurring task. */
private final String recurringTaskID;
- // The fully-qualified name of the class that will be used to implement the
- // class.
+ /**
+ * The fully-qualified name of the class that will be used to implement the
+ * class.
+ */
private final String taskClassName;
- // Task instance.
+ /** Task instance. */
private Task task;
- // Task scheduler for this task.
+ /** Task scheduler for this task. */
private final TaskScheduler taskScheduler;
- // Number of tokens in the task schedule tab.
+ /** Number of tokens in the task schedule tab. */
private static final int TASKTAB_NUM_TOKENS = 5;
- // Maximum year month days.
+ /** Maximum year month days. */
static final int MONTH_LENGTH[]
= {31,28,31,30,31,30,31,31,30,31,30,31};
- // Maximum leap year month days.
+ /** Maximum leap year month days. */
static final int LEAP_MONTH_LENGTH[]
= {31,29,31,30,31,30,31,31,30,31,30,31};
- /**
- * Task tab fields.
- */
+ /** Task tab fields. */
private static enum TaskTab {MINUTE, HOUR, DAY, MONTH, WEEKDAY};
- private final static int MINUTE_INDEX = 0;
- private final static int HOUR_INDEX = 1;
- private final static int DAY_INDEX = 2;
- private final static int MONTH_INDEX = 3;
- private final static int WEEKDAY_INDEX = 4;
+ private static final int MINUTE_INDEX = 0;
+ private static final int HOUR_INDEX = 1;
+ private static final int DAY_INDEX = 2;
+ private static final int MONTH_INDEX = 3;
+ private static final int WEEKDAY_INDEX = 4;
- // Exact match pattern.
- private static final Pattern exactPattern =
- Pattern.compile("\\d+");
+ /** Wildcard match pattern. */
+ private static final Pattern wildcardPattern = Pattern.compile("^\\*(?:/(\\d+))?");
- // Range match pattern.
- private static final Pattern rangePattern =
- Pattern.compile("\\d+[-]\\d+");
+ /** Exact match pattern. */
+ private static final Pattern exactPattern = Pattern.compile("(\\d+)");
- // List match pattern.
- private static final Pattern listPattern =
- Pattern.compile("^(\\d+,)(.*)(\\d+)$");
+ /** Range match pattern. */
+ private static final Pattern rangePattern = Pattern.compile("(\\d+)-(\\d+)(?:/(\\d+))?");
- // Boolean arrays holding task tab slots.
+ /** Boolean arrays holding task tab slots. */
private final boolean[] minutesArray;
private final boolean[] hoursArray;
private final boolean[] daysArray;
@@ -612,6 +603,7 @@
/**
* 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.
@@ -623,57 +615,82 @@
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++) {
+ // Wildcard with optional increment.
+ Matcher m = wildcardPattern.matcher(tabField);
+ if (m.matches() && m.groupCount() == 1)
+ {
+ String stepString = m.group(1);
+ int increment = isValueAbsent(stepString) ? 1 : Integer.parseInt(stepString);
+ for (int i = minValue; i <= maxValue; i += increment)
+ {
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 {
+ for (String listVal : tabField.split(","))
+ {
+ // Single number.
+ m = exactPattern.matcher(listVal);
+ if (m.matches() && m.groupCount() == 1)
+ {
+ String exactValue = m.group(1);
+ if (isValueAbsent(exactValue))
+ {
throw new IllegalArgumentException();
}
+ int value = Integer.parseInt(exactValue);
+ if (value < minValue || value > maxValue)
+ {
+ throw new IllegalArgumentException();
+ }
+ valueList[value] = true;
+ continue;
}
- return valueList;
+
+ // Range of numbers with optional increment.
+ m = rangePattern.matcher(listVal);
+ if (m.matches() && m.groupCount() == 3) {
+ String startString = m.group(1);
+ String endString = m.group(2);
+ String stepString = m.group(3);
+ int increment = isValueAbsent(stepString) ? 1 : Integer.parseInt(stepString);
+ if (isValueAbsent(startString) || isValueAbsent(endString))
+ {
+ throw new IllegalArgumentException();
+ }
+ int startValue = Integer.parseInt(startString);
+ int endValue = Integer.parseInt(endString);
+ if (startValue > endValue || startValue < minValue || endValue > maxValue)
+ {
+ throw new IllegalArgumentException();
+ }
+ for (int i = startValue; i <= endValue; i += increment)
+ {
+ valueList[i] = true;
+ }
+ continue;
+ }
+
+ // Can only have a list of numbers and ranges.
+ throw new IllegalArgumentException();
}
- throw new IllegalArgumentException();
+ return valueList;
+ }
+
+ /**
+ * Check if a String from a Matcher group is absent. Matcher returns empty strings
+ * for optional groups that are absent.
+ *
+ * @param s A string returned from Matcher.group()
+ * @return true if the string is unusable, false if it is usable.
+ */
+ private static boolean isValueAbsent(String s)
+ {
+ return (s == null || s.length() == 0) ? true : false;
}
/**
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 cc95e13..2eee4fa 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
@@ -22,6 +22,7 @@
*
*
* Copyright 2008-2010 Sun Microsystems, Inc.
+ * Portions Copyright 2014 ForgeRock, AS
*/
package org.opends.server.backends.task;
@@ -413,52 +414,61 @@
@DataProvider(name="recurringTaskSchedules")
public Object[][] createRecurringTaskSchedules() {
return new Object[][] {
- { "* * * *", false },
- { "* * * * * *", false },
- { "*:*:*:*:*", false },
- { "60 * * * *", false },
- { "-1 * * * *", false },
- { "1-60 * * * *", false },
- { "1,60 * * * *", false },
- { "* 24 * * *", false },
- { "* -1 * * *", false },
- { "* 1-24 * * *", false },
- { "* 1,24 * * *", false },
- { "* * 32 * *", false },
- { "* * 0 * *", false },
- { "* * 1-32 * *", false },
- { "* * 1,32 * *", false },
- { "* * * 13 *", false },
- { "* * * 0 *", false },
- { "* * * 1-13 *", false },
- { "* * * 1,13 *", false },
- { "* * * * 7", false },
- { "* * * * -1", false },
- { "* * * * 1-7", false },
- { "* * * * 1,7", false },
- { "* * 31 2 *", false },
- { "* * 29 2 *", true },
- { "* * * * *", true },
- { "59 * * * *", true },
- { "0 * * * *", true },
- { "0-59 * * * *", true },
- { "0,59 * * * *", true },
- { "* 23 * * *", true },
- { "* 0 * * *", true },
- { "* 0-23 * * *", true },
- { "* 0,23 * * *", true },
- { "* * 31 * *", true },
- { "* * 1 * *", true },
- { "* * 1-31 * *", true },
- { "* * 1,31 * *", true },
- { "* * * 12 *", true },
- { "* * * 1 *", true },
- { "* * * 1-12 *", true },
- { "* * * 1,12 *", true },
- { "* * * * 6", true },
- { "* * * * 0", true },
- { "* * * * 0-6", true },
- { "* * * * 0,6", true }
+ { "* * * *", false },
+ { "* * * * * *", false },
+ { "*:*:*:*:*", false },
+ { "60 * * * *", false },
+ { "-1 * * * *", false },
+ { "1-60 * * * *", false },
+ { "1,60 * * * *", false },
+ { "* 24 * * *", false },
+ { "* -1 * * *", false },
+ { "* 1-24 * * *", false },
+ { "* 1,24 * * *", false },
+ { "* * 32 * *", false },
+ { "* * 0 * *", false },
+ { "* * 1-32 * *", false },
+ { "* * 1,32 * *", false },
+ { "* * * 13 *", false },
+ { "* * * 0 *", false },
+ { "* * * 1-13 *", false },
+ { "* * * 1,13 *", false },
+ { "* * * * 7", false },
+ { "* * * * -1", false },
+ { "* * * * 1-7", false },
+ { "* * * * 1,7", false },
+ { "* * 31 2 *", false },
+ { "*/foo * * * *", false },
+ { "1-3,10/4,13 * * * *", false },
+ { "1-5/,10,13 * * * *", false },
+ { "1-5/foo,10,13 * * * *", false },
+ { "* * 29 2 *", true },
+ { "* * * * *", true },
+ { "59 * * * *", true },
+ { "0 * * * *", true },
+ { "0-59 * * * *", true },
+ { "0,59 * * * *", true },
+ { "* 23 * * *", true },
+ { "* 0 * * *", true },
+ { "* 0-23 * * *", true },
+ { "* 0,23 * * *", true },
+ { "* * 31 * *", true },
+ { "* * 1 * *", true },
+ { "* * 1-31 * *", true },
+ { "* * 1,31 * *", true },
+ { "* * * 12 *", true },
+ { "* * * 1 *", true },
+ { "* * * 1-12 *", true },
+ { "* * * 1,12 *", true },
+ { "* * * * 6", true },
+ { "* * * * 0", true },
+ { "* * * * 0-6", true },
+ { "* * * * 0,6", true },
+ { "*/2 * * * *", true },
+ { "1-3,10-13 * * * *", true },
+ { "1-3,10,13 * * * *", true },
+ { "1-5/2,10,13 * * * *", true },
+ { "1-5/2,11-15/2 * * * *", true }
};
}
--
Gitblit v1.10.0