web-dev-qa-db-ja.com

cron式の検証がJava

Java Quartzを使用してスケジューリングアプリケーションを作成しています。CronTriggerを使用していますが、私のcron式は、スケジュールされる前にデータベースに入力され、ユーザー入力に基づいています。

取得したcron式が有効であることを確認する方法はありますか?スケジューラを実行してトリガーを作成しようとするとParseExceptionが発生するまで待つのではなく、これを実行してユーザーに適切なエラーメッセージを表示したいのです。ユーザーがデータを入力してから数日になる可能性があります。

29
Omar Kooheji

実際に実行せずに単純にトリガーを作成することはできませんか? ParseExceptionが発生した場合は、適切なフィードバックを提供するだけで済みます。式に問題がない場合は、式をDBに永続化します。

編集:または単にこれを行います:

org.quartz.CronExpression.isValidExpression(expression);
43
sfussenegger

次の code を変更し、@ 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;
}

私は決して正規表現の専門家ではありませんが、少なくともこれは、クオーツによって提供されるすべての例で機能するようです ドキュメント

16
Leo

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
kekec

org.quartzを使用している場合は、次のようにCron式を検証できます。

try {
    CronExpression cron = new CronExpression(cronExpression);
    ...
} catch (ParseException e) {
    //exception handling
}

公式APIドキュメントから引用 Class CronExpression

public CronExpression(String cronExpression)スローParseException
指定されたパラメーターに基づいて新しいCronExpressionを構築します。
パラメーター:
cronExpression-新しいオブジェクトが表すcron式の文字列表現
スロー:
ParseException-文字列式を有効なCronExpressionに解析できない場合

0
LHCHIN

Githubの「QuartzNet」プロジェクトで次の正規表現を見つけました。 Quartzが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])))?)?)
0
jovanchohan