| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.sling.event.impl.support; |
| |
| import java.io.Serializable; |
| import java.text.ParseException; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.List; |
| |
| import org.apache.sling.event.jobs.ScheduleInfo; |
| import org.quartz.CronExpression; |
| |
| public class ScheduleInfoImpl implements ScheduleInfo, Serializable { |
| |
| private static final long serialVersionUID = 1L; |
| |
| /** Serialization version. */ |
| private static final String VERSION = "1"; |
| |
| public static ScheduleInfoImpl HOURLY(final int minutes) { |
| return new ScheduleInfoImpl(ScheduleType.HOURLY, -1, -1, minutes, null, -1, null); |
| } |
| |
| public static ScheduleInfoImpl CRON(final String expr) { |
| return new ScheduleInfoImpl(ScheduleType.CRON, -1, -1, -1, null, -1, expr); |
| } |
| |
| public static ScheduleInfoImpl AT(final Date at) { |
| return new ScheduleInfoImpl(ScheduleType.DATE, -1, -1, -1, at, -1, null); |
| } |
| |
| public static ScheduleInfoImpl YEARLY(final int month, final int day, final int hour, final int minute) { |
| return new ScheduleInfoImpl(ScheduleType.YEARLY, day, hour, minute, null, month, null); |
| } |
| |
| public static ScheduleInfoImpl MONTHLY(final int day, final int hour, final int minute) { |
| return new ScheduleInfoImpl(ScheduleType.MONTHLY, day, hour, minute, null, -1, null); |
| } |
| |
| public static ScheduleInfoImpl WEEKLY(final int day, final int hour, final int minute) { |
| return new ScheduleInfoImpl(ScheduleType.WEEKLY, day, hour, minute, null, -1, null); |
| } |
| |
| public static ScheduleInfoImpl DAILY(final int hour, final int minute) { |
| return new ScheduleInfoImpl(ScheduleType.DAILY, -1, hour, minute, null, -1, null); |
| } |
| |
| private final ScheduleType scheduleType; |
| |
| private final int dayOfWeek; |
| |
| private final int hourOfDay; |
| |
| private final int minuteOfHour; |
| |
| private final Date at; |
| |
| private final int monthOfYear; |
| |
| private final String expression; |
| |
| private ScheduleInfoImpl(final ScheduleType scheduleType, |
| final int dayOfWeek, |
| final int hourOfDay, |
| final int minuteOfHour, |
| final Date at, |
| final int monthOfYear, |
| final String expression) { |
| this.scheduleType = scheduleType; |
| this.dayOfWeek = dayOfWeek; |
| this.hourOfDay = hourOfDay; |
| this.minuteOfHour = minuteOfHour; |
| this.at = at; |
| this.monthOfYear = monthOfYear; |
| this.expression = expression; |
| } |
| |
| public static ScheduleInfoImpl deserialize(final ScheduleType scheduleType, final String s) { |
| final String[] parts = s.split("|"); |
| if ( scheduleType == ScheduleType.YEARLY && parts.length == 4 ) { |
| try { |
| return new ScheduleInfoImpl(scheduleType, |
| Integer.parseInt(parts[0]), |
| Integer.parseInt(parts[1]), |
| Integer.parseInt(parts[2]), |
| null, |
| Integer.parseInt(parts[3]), |
| null); |
| } catch ( final IllegalArgumentException iae) { |
| // ignore and return null |
| } |
| } else if ( scheduleType == ScheduleType.MONTHLY && parts.length == 3 ) { |
| try { |
| return new ScheduleInfoImpl(scheduleType, |
| Integer.parseInt(parts[0]), |
| Integer.parseInt(parts[1]), |
| Integer.parseInt(parts[2]), |
| null, |
| -1, |
| null); |
| } catch ( final IllegalArgumentException iae) { |
| // ignore and return null |
| } |
| } else if ( scheduleType == ScheduleType.WEEKLY && parts.length == 3 ) { |
| try { |
| return new ScheduleInfoImpl(scheduleType, |
| Integer.parseInt(parts[0]), |
| Integer.parseInt(parts[1]), |
| Integer.parseInt(parts[2]), |
| null, |
| -1, |
| null); |
| } catch ( final IllegalArgumentException iae) { |
| // ignore and return null |
| } |
| } else if ( scheduleType == ScheduleType.DAILY && parts.length == 2 ) { |
| try { |
| return new ScheduleInfoImpl(scheduleType, |
| -1, |
| Integer.parseInt(parts[0]), |
| Integer.parseInt(parts[1]), |
| null, |
| -1, |
| null); |
| } catch ( final IllegalArgumentException iae) { |
| // ignore and return null |
| } |
| } else if ( scheduleType == ScheduleType.HOURLY && parts.length == 1 ) { |
| try { |
| return new ScheduleInfoImpl(scheduleType, |
| -1, |
| -1, |
| Integer.parseInt(parts[0]), |
| null, |
| -1, |
| null); |
| } catch ( final IllegalArgumentException iae) { |
| // ignore and return null |
| } |
| } else if ( scheduleType == ScheduleType.CRON && parts.length == 1 ) { |
| try { |
| return new ScheduleInfoImpl(scheduleType, |
| -1, |
| -1, |
| -1, |
| null, |
| -1, |
| parts[0]); |
| } catch ( final IllegalArgumentException iae) { |
| // ignore and return null |
| } |
| } |
| |
| return null; |
| } |
| |
| public static ScheduleInfoImpl deserialize(final String s) { |
| final String[] parts = s.split("\\|"); |
| if ( parts.length == 8 && parts[0].equals(VERSION) ) { |
| try { |
| return new ScheduleInfoImpl(ScheduleType.valueOf(parts[1]), |
| Integer.parseInt(parts[2]), |
| Integer.parseInt(parts[3]), |
| Integer.parseInt(parts[4]), |
| (parts[5].equals("null") ? null : new Date(Long.parseLong(parts[5]))), |
| Integer.parseInt(parts[6]), |
| (parts[7].equals("null") ? null : parts[7]) |
| ); |
| } catch ( final IllegalArgumentException iae) { |
| // ignore and return null |
| } |
| } |
| return null; |
| } |
| |
| public String getSerializedString() { |
| final StringBuilder sb = new StringBuilder(); |
| sb.append(VERSION); |
| sb.append("|"); |
| sb.append(this.scheduleType.name()); |
| sb.append("|"); |
| sb.append(String.valueOf(this.dayOfWeek)); |
| sb.append("|"); |
| sb.append(String.valueOf(this.hourOfDay)); |
| sb.append("|"); |
| sb.append(String.valueOf(this.minuteOfHour)); |
| sb.append("|"); |
| if ( at == null ) { |
| sb.append("null"); |
| } else { |
| sb.append(String.valueOf(at.getTime())); |
| } |
| sb.append("|"); |
| sb.append(String.valueOf(this.monthOfYear)); |
| sb.append("|"); |
| if ( expression == null ) { |
| sb.append("null"); |
| } else { |
| sb.append(String.valueOf(expression)); |
| } |
| return sb.toString(); |
| } |
| |
| @Override |
| public ScheduleType getType() { |
| return this.scheduleType; |
| } |
| |
| @Override |
| public Date getAt() { |
| return this.at; |
| } |
| |
| @Override |
| public int getDayOfWeek() { |
| return (this.scheduleType == ScheduleType.WEEKLY ? this.dayOfWeek : -1); |
| } |
| |
| @Override |
| public int getHourOfDay() { |
| return this.hourOfDay; |
| } |
| |
| @Override |
| public int getMinuteOfHour() { |
| return this.minuteOfHour; |
| } |
| |
| @Override |
| public String getExpression() { |
| return this.expression; |
| } |
| |
| @Override |
| public int getMonthOfYear() { |
| return this.monthOfYear; |
| } |
| |
| @Override |
| public int getDayOfMonth() { |
| return (this.scheduleType == ScheduleType.MONTHLY |
| || this.scheduleType == ScheduleType.YEARLY ? this.dayOfWeek : -1); |
| } |
| |
| public void check(final List<String> errors) { |
| switch ( this.scheduleType ) { |
| case DAILY : if ( hourOfDay < 0 || hourOfDay > 23 || minuteOfHour < 0 || minuteOfHour > 59 ) { |
| errors.add("Wrong time information : " + minuteOfHour + ":" + minuteOfHour); |
| } |
| break; |
| case DATE : if ( at == null || at.getTime() <= System.currentTimeMillis() + 2000 ) { |
| errors.add("Date must be in the future : " + at); |
| } |
| break; |
| case HOURLY : if ( minuteOfHour < 0 || minuteOfHour > 59 ) { |
| errors.add("Minute must be between 0 and 59 : " + minuteOfHour); |
| } |
| break; |
| case WEEKLY : if ( hourOfDay < 0 || hourOfDay > 23 || minuteOfHour < 0 || minuteOfHour > 59 ) { |
| errors.add("Wrong time information : " + minuteOfHour + ":" + minuteOfHour); |
| } |
| if ( dayOfWeek < 1 || dayOfWeek > 7 ) { |
| errors.add("Day must be between 1 and 7 : " + dayOfWeek); |
| } |
| break; |
| case MONTHLY : if ( hourOfDay < 0 || hourOfDay > 23 || minuteOfHour < 0 || minuteOfHour > 59 ) { |
| errors.add("Wrong time information : " + minuteOfHour + ":" + minuteOfHour); |
| } |
| if ( dayOfWeek < 1 || dayOfWeek > 28 ) { |
| errors.add("Day must be between 1 and 28 : " + dayOfWeek); |
| } |
| break; |
| case YEARLY : if ( hourOfDay < 0 || hourOfDay > 23 || minuteOfHour < 0 || minuteOfHour > 59 ) { |
| errors.add("Wrong time information : " + minuteOfHour + ":" + minuteOfHour); |
| } |
| if ( dayOfWeek < 1 || dayOfWeek > 28 ) { |
| errors.add("Day must be between 1 and 28 : " + dayOfWeek); |
| } |
| if ( monthOfYear < 1 || monthOfYear > 12 ) { |
| errors.add("Month must be between 1 and 12 : " + dayOfWeek); |
| } |
| break; |
| case CRON : if ( expression == null ) { |
| errors.add("Expression must be specified."); |
| } |
| try { |
| new CronExpression(this.expression); |
| } catch (final ParseException e) { |
| errors.add("Expression must be valid: " + this.expression); |
| } |
| } |
| } |
| |
| public Date getNextScheduledExecution() { |
| final Calendar now = Calendar.getInstance(); |
| switch ( this.scheduleType ) { |
| case DATE : return this.at; |
| case DAILY : final Calendar next = Calendar.getInstance(); |
| next.set(Calendar.HOUR_OF_DAY, this.hourOfDay); |
| next.set(Calendar.MINUTE, this.minuteOfHour); |
| if ( next.before(now) ) { |
| next.add(Calendar.DAY_OF_WEEK, 1); |
| } |
| return next.getTime(); |
| case WEEKLY : final Calendar nextW = Calendar.getInstance(); |
| nextW.set(Calendar.HOUR_OF_DAY, this.hourOfDay); |
| nextW.set(Calendar.MINUTE, this.minuteOfHour); |
| nextW.set(Calendar.DAY_OF_WEEK, this.dayOfWeek); |
| if ( nextW.before(now) ) { |
| nextW.add(Calendar.WEEK_OF_YEAR, 1); |
| } |
| return nextW.getTime(); |
| case HOURLY : final Calendar nextH = Calendar.getInstance(); |
| nextH.set(Calendar.MINUTE, this.minuteOfHour); |
| if ( nextH.before(now) ) { |
| nextH.add(Calendar.HOUR_OF_DAY, 1); |
| } |
| return nextH.getTime(); |
| case MONTHLY : final Calendar nextM = Calendar.getInstance(); |
| nextM.set(Calendar.HOUR_OF_DAY, this.hourOfDay); |
| nextM.set(Calendar.MINUTE, this.minuteOfHour); |
| nextM.set(Calendar.DAY_OF_MONTH, this.dayOfWeek); |
| if ( nextM.before(now) ) { |
| nextM.add(Calendar.MONTH, 1); |
| } |
| return nextM.getTime(); |
| case YEARLY : final Calendar nextY = Calendar.getInstance(); |
| nextY.set(Calendar.HOUR_OF_DAY, this.hourOfDay); |
| nextY.set(Calendar.MINUTE, this.minuteOfHour); |
| nextY.set(Calendar.DAY_OF_MONTH, this.dayOfWeek); |
| nextY.set(Calendar.MONTH, this.monthOfYear - 1); |
| if ( nextY.before(now) ) { |
| nextY.add(Calendar.YEAR, 1); |
| } |
| return nextY.getTime(); |
| case CRON : try { |
| final CronExpression exp = new CronExpression(this.expression); |
| return exp.getNextValidTimeAfter(new Date()); |
| } catch (final ParseException e) { |
| // as we check the expression in check() everything should be fine here |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * If the job is scheduled daily or weekly, return the cron expression |
| */ |
| public String getCronExpression() { |
| if ( this.scheduleType == ScheduleType.DAILY ) { |
| final StringBuilder sb = new StringBuilder("0 "); |
| sb.append(String.valueOf(this.minuteOfHour)); |
| sb.append(' '); |
| sb.append(String.valueOf(this.hourOfDay)); |
| sb.append(" * * ?"); |
| return sb.toString(); |
| } else if ( this.scheduleType == ScheduleType.WEEKLY ) { |
| final StringBuilder sb = new StringBuilder("0 "); |
| sb.append(String.valueOf(this.minuteOfHour)); |
| sb.append(' '); |
| sb.append(String.valueOf(this.hourOfDay)); |
| sb.append(" ? * "); |
| sb.append(String.valueOf(this.dayOfWeek)); |
| return sb.toString(); |
| } else if ( this.scheduleType == ScheduleType.HOURLY ) { |
| final StringBuilder sb = new StringBuilder("0 "); |
| sb.append(String.valueOf(this.minuteOfHour)); |
| sb.append(" * * * ?"); |
| return sb.toString(); |
| } else if ( this.scheduleType == ScheduleType.MONTHLY ) { |
| final StringBuilder sb = new StringBuilder("0 "); |
| sb.append(String.valueOf(this.minuteOfHour)); |
| sb.append(' '); |
| sb.append(String.valueOf(this.hourOfDay)); |
| sb.append(' '); |
| sb.append(String.valueOf(this.dayOfWeek)); |
| sb.append(" * ?"); |
| return sb.toString(); |
| } else if ( this.scheduleType == ScheduleType.YEARLY ) { |
| final StringBuilder sb = new StringBuilder("0 "); |
| sb.append(String.valueOf(this.minuteOfHour)); |
| sb.append(' '); |
| sb.append(String.valueOf(this.hourOfDay)); |
| sb.append(' '); |
| sb.append(String.valueOf(this.dayOfWeek)); |
| sb.append(' '); |
| sb.append(String.valueOf(this.monthOfYear - 1)); |
| sb.append(" ?"); |
| return sb.toString(); |
| } else if ( this.scheduleType == ScheduleType.CRON ) { |
| return this.expression; |
| } |
| return null; |
| } |
| |
| @Override |
| public String toString() { |
| return "ScheduleInfo [scheduleType=" + scheduleType |
| + ", dayOfWeek=" + dayOfWeek + ", hourOfDay=" + hourOfDay |
| + ", minuteOfHour=" + minuteOfHour + ", at=" + at |
| + ", monthOfYear=" + monthOfYear + ", expression=" + expression |
| + "]"; |
| } |
| } |