Проверка выражения cron действительна в Java
Я пишу приложение для планирования на Java, используя Quartz. Я использую CronTrigger, но мои выражения cron вводятся в базу данных до того, как они запланированы и основаны на пользовательском вводе.
Есть ли способ проверить, что выражения cron действительны, когда я их фиксирую? Я бы предпочел сделать это и предоставить пользователю соответствующее сообщение об ошибке, чем дождаться запуска планировщика, и я получу ParseException, когда я попытаюсь создать триггер. Которые могут быть через несколько дней после ввода пользователем данных.
Ответы
Ответ 1
Не можете ли вы просто создать триггер, фактически не выполнив его? Вы можете просто дать соответствующую обратную связь в случае исключения ParseException. Если выражение в порядке, сохраняйте выражение в DB.
Изменить: или просто выполните следующее:
org.quartz.CronExpression.isValidExpression(expression);
Ответ 2
Я изменил следующий код, добавленный @ph4r05, чтобы генерировать регулярное выражение; здесь регулярное выражение:
^\s*($|#|\w+\s*=|(\?|\*|(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?(?:,(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?)*)\s+(\?|\*|(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?(?:,(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?)*)\s+(\?|\*|(?:[01]?\d|2[0-3])(?:(?:-|\/|\,)(?:[01]?\d|2[0-3]))?(?:,(?:[01]?\d|2[0-3])(?:(?:-|\/|\,)(?:[01]?\d|2[0-3]))?)*)\s+(\?|\*|(?:0?[1-9]|[12]\d|3[01])(?:(?:-|\/|\,)(?:0?[1-9]|[12]\d|3[01]))?(?:,(?:0?[1-9]|[12]\d|3[01])(?:(?:-|\/|\,)(?:0?[1-9]|[12]\d|3[01]))?)*)\s+(\?|\*|(?:[1-9]|1[012])(?:(?:-|\/|\,)(?:[1-9]|1[012]))?(?:L|W)?(?:,(?:[1-9]|1[012])(?:(?:-|\/|\,)(?:[1-9]|1[012]))?(?:L|W)?)*|\?|\*|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?(?:,(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)*)\s+(\?|\*|(?:[0-6])(?:(?:-|\/|\,|#)(?:[0-6]))?(?:L)?(?:,(?:[0-6])(?:(?:-|\/|\,|#)(?:[0-6]))?(?:L)?)*|\?|\*|(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?(?:,(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?)*)(|\s)+(\?|\*|(?:|\d{4})(?:(?:-|\/|\,)(?:|\d{4}))?(?:,(?:|\d{4})(?:(?:-|\/|\,)(?:|\d{4}))?)*))$
Вот код Java:
private static String cronRegex = null;
public static String getCronRegex()
{
if (cronRegex == null)
{
// numbers intervals and regex
Map<String, String> numbers = new HashMap<String, String>();
numbers.put("sec", "[0-5]?\\d");
numbers.put("min", "[0-5]?\\d");
numbers.put("hour", "[01]?\\d|2[0-3]");
numbers.put("day", "0?[1-9]|[12]\\d|3[01]");
numbers.put("month", "[1-9]|1[012]");
numbers.put("dow", "[0-6]");
numbers.put("year", "|\\d{4}");
Map<String, String> field_re = new HashMap<String, String>();
// expand regex to contain different time specifiers
for (String field : numbers.keySet())
{
String number = numbers.get(field);
String range = "(?:" + number + ")(?:(?:-|\\/|\\," + ("dow".equals(field)? "|#" : "") +
")(?:" + number + "))?" + ("dow".equals(field)? "(?:L)?" : ("month".equals(field)? "(?:L|W)?" : ""));
field_re.put(field, "\\?|\\*|" + range + "(?:," + range + ")*");
}
// add string specifiers
String monthRE = field_re.get("month");
String monthREVal = "JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC";
String monthRERange = "(?:" + monthREVal + ")(?:(?:-)(?:" + monthREVal + "))?" ;
monthRE = monthRE + "|\\?|\\*|" + monthRERange + "(?:," + monthRERange + ")*" ;
field_re.put("month", monthRE);
String dowRE = field_re.get("dow");
String dowREVal = "MON|TUE|WED|THU|FRI|SAT|SUN";
String dowRERange = "(?:" + dowREVal + ")(?:(?:-)(?:" + dowREVal + "))?" ;
dowRE = dowRE + "|\\?|\\*|" + dowRERange + "(?:," + dowRERange + ")*" ;
field_re.put("dow", dowRE);
StringBuilder fieldsReSB = new StringBuilder();
fieldsReSB.append("^\\s*(").append("$") //
.append("|#") //
.append("|\\w+\\s*=") //
.append("|") //
.append("(")//
.append(field_re.get("sec")).append(")\\s+(")//
.append(field_re.get("min")).append(")\\s+(")//
.append(field_re.get("hour")).append(")\\s+(")//
.append(field_re.get("day")).append(")\\s+(")//
.append(field_re.get("month")).append(")\\s+(")//
.append(field_re.get("dow")).append(")(|\\s)+(")//
.append(field_re.get("year"))//
.append(")")//
.append(")")//
.append("$");
cronRegex = fieldsReSB.toString();
}
return cronRegex;
}
Я не эксперт по регулярному выражению, но, по крайней мере, это похоже на все примеры, данные кварцем documentation
Ответ 3
Вы можете использовать cron-utils
Мало того, что проверка cron действительна, но вы можете конвертировать из разных форматов cron в целевую (например: если пользователь вводит выражение Unix cron, вы можете легко преобразовать его в Quartz и сохранить его в DB).
Ниже мы приводим некоторые фрагменты:
// Turn cron expressions into another format by using CronMapper:
CronMapper cronMapper = CronMapper.fromUnixToQuartz();
Cron quartzCron = cronMapper.map(unixCron);
// and to get a String representation of it, we can use
quartzCron.asString();
//validate the cron expression!
quartzCron.validate()
Ответ 4
Я нашел следующее регулярное выражение в проекте QuartzNet на Github. Я думаю, это может быть то, что Кварц использует для проверки выражений cron.
Ссылка: https://github.com/quartznet/quartznet/blob/master/src/Quartz/Xml/job_scheduling_data_2_0.xsd
(((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\?])|([\*]))[\s](((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\?])|([\*]))[\s](((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)|(([\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))|([\?])|([\*]))[\s](((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)|(([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(L(-[0-9])?)|(L(-[1-2][0-9])?)|(L(-[3][0-1])?)|(LW)|([1-9]W)|([1-3][0-9]W)|([\?])|([\*]))[\s](((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)|(([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))|(((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)|((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|([\?])|([\*]))[\s]((([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)|([1-7]/([1-7]))|(((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)|((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|(([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))?(L|LW)?)|(([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)|([\?])|([\*]))([\s]?(([\*])?|(19[7-9][0-9])|(20[0-9][0-9]))?| (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?| ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?)