package org.n3r.quartz.glass.util; import org.apache.commons.lang.StringUtils; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.quartz.CronScheduleBuilder; import org.quartz.ScheduleBuilder; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; public class GlassSchedulerParser { static Pattern fromPattern = Pattern.compile( "\\bfrom\\b\\s*(\\d\\d\\d\\d-\\d\\d-\\d\\d)( \\d\\d:\\d\\d:\\d\\d)?", Pattern.CASE_INSENSITIVE); static Pattern toPattern = Pattern.compile( "\\bto\\b\\s*(\\d\\d\\d\\d-\\d\\d-\\d\\d)( \\d\\d:\\d\\d:\\d\\d)?", Pattern.CASE_INSENSITIVE); private final String schedulerExpr; private String expr; private Date fromDate; private Date toDate; private ScheduleBuilder<? extends Trigger> scheduleBuilder; public GlassSchedulerParser(String schedulerExpr) { if (StringUtils.isBlank(schedulerExpr)) throw new RuntimeException("scheduler expression can not be blank"); this.schedulerExpr = schedulerExpr; this.expr = schedulerExpr.trim(); } public GlassSchedulerParser parse() { fromDate = parseDate(fromPattern, "00:00:00"); if (fromDate != null && fromDate.before(new Date())) fromDate = null; toDate = parseDate(toPattern, "23:59:59"); if (fromDate != null && toDate != null && fromDate.after(toDate)) { throw new RuntimeException("scheduler expression is not valid because of from date is after of to date"); } if (StringUtils.startsWithIgnoreCase(expr, "Every")) { this.scheduleBuilder = parseEveryExpr(expr.substring("Every".length())); } else if (StringUtils.startsWithIgnoreCase(expr, "At")) { this.scheduleBuilder = parseAtExpr(expr.substring("At".length())); } else { this.scheduleBuilder = parseCron(); } return this; } private Date parseDate(Pattern pattern, String defaultTime) { Matcher fromMatcher = pattern.matcher(expr); if (!fromMatcher.find()) return null; String fromDay = fromMatcher.group(1); String timePart = fromMatcher.group(2); String fromTime = timePart == null ? defaultTime : timePart.trim(); expr = StringUtils.substring(expr, 0, fromMatcher.start()) + StringUtils.substring(expr, fromMatcher.end()); expr = expr.trim(); return DateTimeFormat.forPattern("yyyy-MM-ddHH:mm:ss") .parseDateTime(fromDay + fromTime).toDate(); } private CronScheduleBuilder parseCron() { return CronScheduleBuilder .cronSchedule(schedulerExpr) .withMisfireHandlingInstructionIgnoreMisfires(); } static Pattern atExprPattern = Pattern.compile( "\\s+(\\d\\d|\\?\\?):(\\d\\d)", Pattern.CASE_INSENSITIVE); private static ScheduleBuilder<? extends Trigger> parseAtExpr(String atExpr) { Matcher matcher = atExprPattern.matcher(atExpr); if (!matcher.matches()) throw new RuntimeException(atExpr + " is not valid"); if (matcher.group(1).equals("??")) { return CronScheduleBuilder.cronSchedule("0 " + matcher.group(2) + " * * * ?"); } DateTimeFormatter formatter = DateTimeFormat.forPattern("HH:mm"); DateTime dateTime = formatter.parseDateTime(matcher.group().trim()); return CronScheduleBuilder.dailyAtHourAndMinute(dateTime.getHourOfDay(), dateTime.getMinuteOfHour()); } static Pattern everyExprPattern = Pattern.compile( "\\s+(\\d+)\\s*(h|hour|m|minute|s|second)s?", Pattern.CASE_INSENSITIVE); private static ScheduleBuilder<? extends Trigger> parseEveryExpr(String everyExpr) { Matcher matcher = everyExprPattern.matcher(everyExpr); if (!matcher.matches()) throw new RuntimeException(everyExpr + " is not valid"); int num = Integer.parseInt(matcher.group(1)); if (num <= 0) throw new RuntimeException(everyExpr + " is not valid"); char unit = matcher.group(2).charAt(0); TimeUnit timeUnit = TimeUnit.MILLISECONDS; switch (unit) { case 'h': case 'H': timeUnit = TimeUnit.HOURS; break; case 'm': case 'M': timeUnit = TimeUnit.MINUTES; break; case 's': case 'S': timeUnit = TimeUnit.SECONDS; break; default: } return SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds((int) timeUnit.toSeconds(num)) .repeatForever(); } public boolean isToDateInFuture() { return toDate == null || toDate.after(new Date()); } public String getToDateStr() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(toDate); } public Date getFromDate() { return fromDate; } public Date getToDate() { return toDate; } public ScheduleBuilder<? extends Trigger> getScheduleBuilder() { return scheduleBuilder; } }