web-dev-qa-db-fra.com

Vérification d'une expression cron est valide Java

J'écris une application de planification dans Java à l'aide de quartz. J'utilise le crontrigger, mais mes expressions de cron sont entrées dans une base de données avant qu'elles soient planifiées et basées sur la saisie de l'utilisateur.

Existe-t-il une façon de vérifier que les expressions cron sont valables lorsque je les capture? Je préférerais faire cela et donner à l'utilisateur un message d'erreur approprié qu'attendre que le planificateur soit exécuté et que je reçois une parseException lorsque j'essaie de créer la gâchette. Ce qui pourrait être des jours après l'entrée de l'utilisateur les données.

29
Omar Kooheji

Vous ne pouvez pas simplement créer un déclencheur sans l'exécuter réellement? Vous pourriez simplement donner des commentaires appropriés en cas de parseException. Si l'expression est correcte, persister l'expression à DB.

Éditer: ou simplement faire ceci:

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

J'ai modifié ce qui suit code ajouté par @ ph4R05 pour générer une regex; Voici la regex:

^\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}))?)*))$

Voici le Java code:

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;
}

Je ne suis en aucun cas un expert de regex, mais au moins cela semble fonctionner sur tous les exemples donnés par le quartz Documentation

16
Leo

Vous pouvez utiliser cron-utils Vérifiez non seulement que le cron est valide, mais vous pouvez convertir de différents formats de cron à la cible (ex.: Si l'utilisateur saisit une expression de cron unix, vous pourriez facilement Convertir en quartz et persistez celui-ci à dB). Ci-dessous nous fournissons des extraits:

// 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

Si vous utilisez org.quartz, vous pouvez valider une expression de cron comme suit:

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

Cité du document de l'API officiel CroneXpression de classe :

la chantexpression publique (cordes chantexpression) jette parseException
[.____] Construit une nouvelle cronexpression basée sur le paramètre spécifié.
Paramètres:
Cronexpression - Représentation des chaînes de l'expression de cron Le nouvel objet devrait représenter
Jette:
ParseException - Si l'expression de chaîne ne peut pas être analysée dans une chonopression valide

0
LHCHIN

J'ai trouvé l'expression régulière suivante dans le projet "Quartznet" sur GitHub. Je pense que c'est peut-être ce que Quartz utilise pour valider les expressions cron.

Link: 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