All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ow2.easybeans.component.quartz.EasyBeansScheduleTrigger Maven / Gradle / Ivy

/**
 * EasyBeans
 * Copyright (C) 2012 Bull S.A.S.
 * Contact: [email protected]
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.easybeans.component.quartz;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.ejb.ScheduleExpression;

import org.quartz.impl.triggers.CoreTrigger;
import org.quartz.impl.triggers.CronTriggerImpl;

/**
 * Defines a trigger using the new EJB 3.1 Schedule expression.
 * @author Florent Benoit
 */
public class EasyBeansScheduleTrigger extends CronTriggerImpl implements CoreTrigger{

    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = 5986420827628682444L;

    /**
     * Schedule expression used by this trigger.
     */
    private ScheduleExpression scheduleExpression = null;

    /**
     * Schedule expression parser.
     */
    private ScheduleExpressionParser parser = null;

    /**
     * The schedule expression.
     */
    private EasyBeansScheduleExpression easyBeansScheduleExpression;

    /**
     * max loop before trying to find a timeout date.
     */
    private static final int MAX_LOOP = 200;

    /**
     * Build a new trigger for the given expression.
     * @param scheduleExpression the given EJB schedule expression
     */
    public EasyBeansScheduleTrigger(final ScheduleExpression scheduleExpression) {
        super();
        this.scheduleExpression = scheduleExpression;
        this.parser = new ScheduleExpressionParser();
        init();
    }

    /**
     * Initialize the expression.
     */
    protected void init() {

        // now we will get schedule values for each part of the expression
        this.easyBeansScheduleExpression = this.parser.parse(this.scheduleExpression);

    }


    /**
     * Find the next occurence after the given date.
     * @param afterTime the time after which we've to find a new date
     * @return new date
     */
    @Override
    protected Date getTimeAfter(final Date afterTime) {

        int loop = 0;

        // Computation is based on Gregorian year only.
        Calendar afterTimeCalendar = new GregorianCalendar(getTimeZone());

        Calendar originalDate = null;

        // If start time has been specified, we have to compute the next time from this start and not from the original given time
        if (this.scheduleExpression.getStart() != null) {
            afterTimeCalendar.setTime(getStartTime());
            originalDate = (Calendar) afterTimeCalendar.clone();
        } else {
            afterTimeCalendar.setTime(afterTime);
            originalDate = (Calendar) afterTimeCalendar.clone();
            // move ahead one second, since we're computing the time *after* the
            // given time
            afterTimeCalendar.add(Calendar.SECOND, 1);
        }

        // CronTrigger does not deal with milliseconds
        afterTimeCalendar.set(Calendar.MILLISECOND, 0);
        afterTimeCalendar.setFirstDayOfWeek(Calendar.SUNDAY);

        boolean daysDone = false;
        boolean gotOne = false;

        // loop until we've computed the next time, or we've past the endTime
        while (!gotOne) {
            loop++;

            // Unable to find a value (should be unable to find it !)
            if (loop > MAX_LOOP) {
                return null;
            }

            ScheduleValue dayOfMonthScheduleValue = this.easyBeansScheduleExpression.getDayOfMonth();
            ScheduleValue dayOfWeekScheduleValue = this.easyBeansScheduleExpression.getDayOfWeek();


            /**
             * Seconds
             */
            ValueResult valueResult = this.easyBeansScheduleExpression.getSecond().getTimeAfter(afterTimeCalendar);
            int previousSeconds = afterTimeCalendar.get(Calendar.SECOND);
            if (valueResult.needsIncrement()) {
                // increment minute
                afterTimeCalendar.add(Calendar.MINUTE, 1);
            }
            // Value has been changed
            if (previousSeconds != valueResult.getResult() || valueResult.needsIncrement()) {
                // Set the second field to the found value
                afterTimeCalendar.set(Calendar.SECOND, valueResult.getResult());
                //continue;
            }


            /**
             * Minutes
             */
            valueResult = this.easyBeansScheduleExpression.getMinute().getTimeAfter(afterTimeCalendar);
            int previousMinutes = afterTimeCalendar.get(Calendar.MINUTE);
            if (valueResult.needsIncrement()) {
                // increment hour
                afterTimeCalendar.add(Calendar.HOUR, 1);
            }
            // Value has been changed
            if (previousMinutes != valueResult.getResult() || valueResult.needsIncrement()) {
                // Set the minute field to the found value
                afterTimeCalendar.set(Calendar.MINUTE, valueResult.getResult());

                // Change to 0
                afterTimeCalendar.set(Calendar.SECOND, 0);
                continue;
            }

            /**
             * Hour
             */
            valueResult = this.easyBeansScheduleExpression.getHour().getTimeAfter(afterTimeCalendar);
            int previousHours = afterTimeCalendar.get(Calendar.HOUR_OF_DAY);
            if (valueResult.needsIncrement() && dayOfWeekScheduleValue instanceof ScheduleValueWildCard && dayOfMonthScheduleValue instanceof ScheduleValueWildCard) {
                // increment day
                afterTimeCalendar.add(Calendar.DAY_OF_MONTH, 1);
            }
            // Value has been changed
            if (previousHours != valueResult.getResult() || valueResult.needsIncrement()) {
                // Set the hour field to the found value
                afterTimeCalendar.set(Calendar.HOUR_OF_DAY, valueResult.getResult());

                // Change to 0
                afterTimeCalendar.set(Calendar.SECOND, 0);
                afterTimeCalendar.set(Calendar.MINUTE, 0);
                continue;
            }


            /**
             * Month
             */
            valueResult = this.easyBeansScheduleExpression.getMonth().getTimeAfter(afterTimeCalendar);
            int previousMonth = afterTimeCalendar.get(Calendar.MONTH);
            Date startTime = this.scheduleExpression.getStart();


            if (valueResult.needsIncrement()) {
                // increment year
                afterTimeCalendar.add(Calendar.YEAR, 1);
            }
            // Value has been changed
            if (previousMonth != valueResult.getResult() || valueResult.needsIncrement()) {
                // Set the hour field to the found value
                afterTimeCalendar.set(Calendar.MONTH, valueResult.getResult());

                // Change to 0
                afterTimeCalendar.set(Calendar.SECOND, 0);
                afterTimeCalendar.set(Calendar.MINUTE, 0);
                afterTimeCalendar.set(Calendar.HOUR_OF_DAY, 0);
                afterTimeCalendar.set(Calendar.DAY_OF_MONTH, 1);
                daysDone = false;
                continue;
            }

            /**
             * Year
             */
            valueResult = this.easyBeansScheduleExpression.getYear().getTimeAfter(afterTimeCalendar);
            int previousYear = afterTimeCalendar.get(Calendar.YEAR);
            if (valueResult.needsIncrement()) {
                // increment year
                afterTimeCalendar.add(Calendar.YEAR, 1);
            }
            // Value has been changed
            if (previousYear != valueResult.getResult() || valueResult.needsIncrement()) {
                // Set the hour field to the found value
                afterTimeCalendar.set(Calendar.YEAR, valueResult.getResult());

                // Change to 0
                afterTimeCalendar.set(Calendar.SECOND, 0);
                afterTimeCalendar.set(Calendar.MINUTE, 0);
                afterTimeCalendar.set(Calendar.HOUR_OF_DAY, 0);
                afterTimeCalendar.set(Calendar.MONTH, 0);
                afterTimeCalendar.set(Calendar.DAY_OF_MONTH, 1);
                daysDone = false;
                continue;
            }




            // dayOfMonth != "*" and dayOfWeek = "*"
            if (!daysDone) {
                if (!(dayOfMonthScheduleValue instanceof ScheduleValueWildCard) && dayOfWeekScheduleValue instanceof ScheduleValueWildCard) {


                    /**
                     * We have only a day-Of-Month value
                     */
                    valueResult = dayOfMonthScheduleValue.getTimeAfter(afterTimeCalendar);
                    int previousDayOfMonth = afterTimeCalendar.get(Calendar.DAY_OF_MONTH);

                    if (startTime != null && startTime.after(afterTimeCalendar.getTime())) {
                        valueResult.setNeedsIncrement(true);
                    }

                    if (valueResult.needsIncrement()) {
                        // increment month
                        afterTimeCalendar.add(Calendar.MONTH, 1);
                    }

                    // Value has been changed
                    if (previousDayOfMonth != valueResult.getResult() || valueResult.needsIncrement()) {
                        // Set the dayOfMonth field to the found value
                        afterTimeCalendar.set(Calendar.DAY_OF_MONTH, valueResult.getResult());


                        // Change to 0
                        afterTimeCalendar.set(Calendar.SECOND, 0);
                        afterTimeCalendar.set(Calendar.MINUTE, 0);
                        afterTimeCalendar.set(Calendar.HOUR_OF_DAY, 0);
                    }
                } else if ((dayOfMonthScheduleValue instanceof ScheduleValueWildCard) && !(dayOfWeekScheduleValue instanceof ScheduleValueWildCard)) {
                    // dayOfMonth == "*" and dayOfWeek != "*"

                    /**
                     * We have only a day-Of-Week value
                     */

                    valueResult = dayOfWeekScheduleValue.getTimeAfter(afterTimeCalendar);
                    int previousDayOfWeek = afterTimeCalendar.get(Calendar.DAY_OF_WEEK);


                    if (valueResult.needsIncrement())  {
                        // increment week in the month
                        afterTimeCalendar.add(Calendar.DAY_OF_WEEK_IN_MONTH, 1);
                    }



                    Calendar now = new GregorianCalendar();
                    now.set(Calendar.MILLISECOND, 0);

                    Date previousFireTime = getPreviousFireTime();
                    if (afterTimeCalendar.equals(now) && previousFireTime == null && afterTimeCalendar.equals(originalDate)) {

                        // needs to report to the next week
                        afterTimeCalendar.add(Calendar.DAY_OF_WEEK_IN_MONTH, 1);

                        if (this.easyBeansScheduleExpression.getMonth() instanceof ScheduleValueAttribute && this.easyBeansScheduleExpression.getYear() instanceof ScheduleValueAttribute) {
                            int wantedMonth = Integer.parseInt(this.scheduleExpression.getMonth());
                            int currentMonth = afterTimeCalendar.get(Calendar.MONTH) + 1;
                            // It means that we can't go further so step back
                            if (wantedMonth != currentMonth) {
                                afterTimeCalendar.add(Calendar.DAY_OF_WEEK_IN_MONTH, -1);
                                return afterTimeCalendar.getTime();
                            }
                        }


                        return afterTimeCalendar.getTime();
                    }

                    // We're past than the original date so we need to upgrade the day of week
                    if (originalDate.after(afterTimeCalendar)) {
                        afterTimeCalendar.add(Calendar.DAY_OF_WEEK_IN_MONTH, 1);

                        // Check that it's still a valid value
                        if (this.easyBeansScheduleExpression.getMonth() instanceof ScheduleValueAttribute && this.easyBeansScheduleExpression.getYear() instanceof ScheduleValueAttribute) {
                            int wantedMonth = Integer.parseInt(this.scheduleExpression.getMonth());
                            int currentMonth = afterTimeCalendar.get(Calendar.MONTH) + 1;
                            // It means that we can't go further so step back
                            if (wantedMonth != currentMonth) {
                                afterTimeCalendar.add(Calendar.DAY_OF_WEEK_IN_MONTH, -1);
                                return afterTimeCalendar.getTime();
                            }
                        }

                        return afterTimeCalendar.getTime();
                    }




                    // Value has been changed
                    if (previousDayOfWeek != valueResult.getResult() || valueResult.needsIncrement()) {
                        // Set the dayOfWeek field to the found value
                        afterTimeCalendar.set(Calendar.DAY_OF_WEEK, valueResult.getResult());


                        // Change to 0
                        afterTimeCalendar.set(Calendar.SECOND, 0);
                        afterTimeCalendar.set(Calendar.MINUTE, 0);
                        afterTimeCalendar.set(Calendar.HOUR_OF_DAY, 0);
                    }

                } else if (dayOfMonthScheduleValue instanceof ScheduleValueWildCard && dayOfWeekScheduleValue instanceof ScheduleValueWildCard) {
                    // We do nothing as we've a wildcard

                } else {
                    // dayOfMonth != "*" and dayOfWeek != "*" (we've a value for the both values
                    // For this case we've to match one of the value which is the first coming date

                    ValueResult valueResultDayOfMonth = dayOfMonthScheduleValue.getTimeAfter(afterTimeCalendar);
                    ValueResult valueResultDayOfWeek = dayOfWeekScheduleValue.getTimeAfter(afterTimeCalendar);

                    // which one is the value that is matching today ?
                    int currentDayOfMonth = afterTimeCalendar.get(Calendar.DAY_OF_MONTH);
                    int currentDayOfWeek = afterTimeCalendar.get(Calendar.DAY_OF_WEEK);

                    // We're matching the day of month
                    if (currentDayOfMonth == valueResultDayOfMonth.getResult()) {
                        // Set the dayOfMonth field to the found value
                        afterTimeCalendar.set(Calendar.DAY_OF_MONTH, valueResultDayOfMonth.getResult());
                    } else if (currentDayOfWeek == valueResultDayOfWeek.getResult()) {
                        //We're matching the day of week

                        // Set the dayOfWeek field to the found value
                        afterTimeCalendar.set(Calendar.DAY_OF_WEEK, valueResultDayOfWeek.getResult());
                    } else {
                        // No match
                        throw new IllegalStateException("No expression matching the current day of week and day of month");
                    }
                }

                daysDone = true;
                continue;
            }


            gotOne = true;
        }

        return checkIfBeforeEnd(updateIfAlreadySent(afterTimeCalendar.getTime()));
    }

    /**
     * Ensure the given date is before the end.
     * @param fireTime the given date
     * @return the given date of null if before
     */
    protected Date checkIfBeforeEnd(final Date fireTime) {
        if (fireTime == null) {
            return null;
        }

        Date end = this.scheduleExpression.getEnd();
        if (end == null) {
            return fireTime;
        }
        // It will never expires
        if (end.before(fireTime)) {
            this.easyBeansTimer.setInvalid();
            return null;
        }
        return fireTime;
    }


    /**
     * @param fireTime the expected date
     * @return null if this value has already been sent
     */
    protected Date updateIfAlreadySent(final Date fireTime) {
        Date previousTime = getPreviousFireTime();
        if (previousTime != null) {
            // We've already sent this value so it's time to stop
            if (fireTime.equals(previousTime)) {
                return null;
            }
        }

        return fireTime;
    }


    /**
     * Clone the current trigger.
     */
    @Override
    public Object clone() {
        EasyBeansScheduleTrigger copy = (EasyBeansScheduleTrigger) super.clone();
        if (this.easyBeansTimer != null) {
            copy.setEasyBeansTimer(this.easyBeansTimer);
        }
        return copy;
    }


    private EasyBeansTimer easyBeansTimer = null;

    public EasyBeansTimer getEasyBeansTimer() {
        return this.easyBeansTimer;
    }


    public void setEasyBeansTimer(final EasyBeansTimer easyBeansTimer) {
        this.easyBeansTimer = easyBeansTimer;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy