Repository: jmrozanec/cron-utils
Pull request #373 (2019-04-07 09:20:36)

Add optional jsr 303 validator.
If you want to use it in your project, you'll have to provide the
javax.validation validation-api library on the classpath.

Only supports the definitions provided by CronDefinitionBuilder.instanceDefinitionFor(CronType).


Patch:
diff --git a/pom.xml b/pom.xml
index d1dc6bc..c49186c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -76,6 +76,12 @@
             <artifactId>slf4j-api</artifactId>
             <version>${slf4j.version}</version>
         </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <version>1.1.0.Final</version>
+            <scope>provided</scope>
+        </dependency>
 
         <!--test dependencies-->
         <dependency>
@@ -108,6 +114,18 @@
             <version>2.2.3</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-validator</artifactId>
+            <version>5.2.4.Final</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.el</groupId>
+            <artifactId>javax.el-api</artifactId>
+            <version>2.2.5</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 	<build>
diff --git a/src/main/java/com/cronutils/validation/Cron.java b/src/main/java/com/cronutils/validation/Cron.java
new file mode 100644
index 0000000..d022e1f
--- /dev/null
+++ b/src/main/java/com/cronutils/validation/Cron.java
@@ -0,0 +1,24 @@
+package com.cronutils.validation;
+
+import com.cronutils.model.CronType;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = CronValidator.class)
+@Inherited
+@Documented
+public @interface Cron {
+
+    String message() default "UNUSED";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+
+    CronType type();
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/cronutils/validation/CronValidator.java b/src/main/java/com/cronutils/validation/CronValidator.java
new file mode 100644
index 0000000..bf7350c
--- /dev/null
+++ b/src/main/java/com/cronutils/validation/CronValidator.java
@@ -0,0 +1,37 @@
+package com.cronutils.validation;
+
+import com.cronutils.model.CronType;
+import com.cronutils.model.definition.CronDefinition;
+import com.cronutils.model.definition.CronDefinitionBuilder;
+import com.cronutils.parser.CronParser;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+public class CronValidator implements ConstraintValidator<Cron, String> {
+
+    private CronType type;
+
+    @Override
+    public void initialize(Cron constraintAnnotation) {
+        this.type = constraintAnnotation.type();
+    }
+
+    @Override
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        if (value == null) {
+            return true;
+        }
+
+        CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(type);
+        CronParser cronParser = new CronParser(cronDefinition);
+        try {
+            cronParser.parse(value).validate();
+            return true;
+        } catch (IllegalArgumentException e) {
+            context.disableDefaultConstraintViolation();
+            context.buildConstraintViolationWithTemplate(e.getMessage()).addConstraintViolation();
+            return false;
+        }
+    }
+}


Test patch:
diff --git a/src/test/java/com/cronutils/validation/CronValidatorTest.java b/src/test/java/com/cronutils/validation/CronValidatorTest.java
new file mode 100644
index 0000000..33f6415
--- /dev/null
+++ b/src/test/java/com/cronutils/validation/CronValidatorTest.java
@@ -0,0 +1,70 @@
+package com.cronutils.validation;
+
+import com.cronutils.model.CronType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+@RunWith(Parameterized.class)
+public class CronValidatorTest {
+
+    private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
+
+    private final String expression;
+    private final boolean valid;
+
+    public CronValidatorTest(String expression, boolean valid) {
+        this.expression = expression;
+        this.valid = valid;
+    }
+
+    @Parameterized.Parameters(name = "{0} ")
+    public static Object[] expressions() {
+        return new Object[][]{
+                {"0 0 * * * *", true},
+                {"*/10 * * * * *", true},
+                {"0 0 8-10 * * *", true},
+                {"0 0 6,19 * * *", true},
+                {"0 0/30 8-10 * * *", true},
+                {"0 0 9-17 * * MON-FRI", true},
+                {"0 0 0 25 12 ?", true},
+                {"0 0 0 L 12 ?", false},
+                {"1,2, * * * * *", false},
+                {"1- * * * * *", false}
+        };
+    }
+
+    @Test
+    public void validateExamples() {
+        TestPojo testPojo = new TestPojo(expression);
+        Set<ConstraintViolation<TestPojo>> violations = validator.validate(testPojo);
+
+        if (valid) {
+            assertTrue(violations.isEmpty());
+        } else {
+            assertFalse(violations.isEmpty());
+        }
+    }
+
+    public static class TestPojo {
+        @Cron(type = CronType.SPRING)
+        private final String cron;
+
+        public TestPojo(String cron) {
+            this.cron = cron;
+        }
+
+        public String getCron() {
+            return cron;
+        }
+
+    }
+}
\ No newline at end of file