com.silverpeas.scheduler.simple.SchedulerJob.java Source code

Java tutorial

Introduction

Here is the source code for com.silverpeas.scheduler.simple.SchedulerJob.java

Source

/**
 * Copyright (C) 2000 - 2013 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception.  You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.silverpeas.scheduler.simple;

import com.silverpeas.scheduler.trigger.CronJobTrigger;
import com.silverpeas.scheduler.JobExecutionContext;
import com.silverpeas.scheduler.ScheduledJob;
import com.silverpeas.scheduler.SchedulerEvent;
import com.silverpeas.scheduler.SchedulerEventListener;
import com.silverpeas.scheduler.SchedulerException;
import com.silverpeas.scheduler.trigger.JobTrigger;
import com.stratelia.silverpeas.silvertrace.SilverTrace;
import java.text.Format;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.commons.lang3.time.FastDateFormat;
import static com.silverpeas.scheduler.SchedulerFactory.*;

/**
 * This is the base class of all scheduler job classes. This class is abstract. If you will
 * implement your own special job class, you have to overrite the method 'execute' and add your own
 * job generation method in the class 'SimpleScheduler'
 */
@Deprecated
abstract public class SchedulerJob implements Runnable, ScheduledJob {
    // Environment variables

    protected Format logDateFormat = FastDateFormat.getInstance("yyyy-MM-dd HH:mm");
    private SchedulerEventListener theOwner;
    // private File theLogBaseFile;
    private String sJobName;
    // private String sJobLogFileName;
    // Converted cron string
    private List<Integer> vMinutes;
    private List<Integer> vHours;
    private List<Integer> vDaysOfMonth;
    private List<Integer> vMonths;
    private List<Integer> vDaysOfWeek;
    // Next timestamp
    private Integer currentMinute;
    private Integer currentHour;
    private Integer currentDayOfMonth;
    private Integer currentMonth;
    private Integer currentYear;
    // Runtime variables
    private long nextTimeStamp = 0;
    private volatile boolean bRunnable;
    private JobTrigger trigger;

    /**
     * This method returns the owner (or creator) of the job
     * @return The owner of the job
     */
    public SchedulerEventListener getOwner() {
        return theOwner;
    }

    public void initTimeStamp(long nextTime) {
        if (nextTime != 0) {
            nextTimeStamp = nextTime;
        } else {
            nextTimeStamp = getNextTimeStamp();
        }
    }

    public long readNextTimeStamp() {
        return nextTimeStamp;
    }

    @Override
    public Date getNextExecutionTime() {
        return new Date(nextTimeStamp);
    }

    @Override
    public long getNexExecutionTimeInMillis() {
        return nextTimeStamp;
    }

    /**
     * This method returns the name of the job
     * @return The name of the job
     */
    public String getJobName() {
        return sJobName;
    }

    /**
     * This method handles the thread execution
     */
    @Override
    public void run() {
        long sleepTime;

        // Get next schedule time
        if (nextTimeStamp == 0) {
            nextTimeStamp = getNextTimeStamp();
        }

        SilverTrace.info(MODULE_NAME, "SchedulerJob.run", "root.MSG_GEN_PARAM_VALUE",
                ": Job '" + sJobName + "' starts without errors.");
        SilverTrace.info(MODULE_NAME, "SchedulerJob.run", "root.MSG_GEN_PARAM_VALUE",
                ": Next schedule time: " + logDateFormat.format(new Date(nextTimeStamp)));

        while (bRunnable) {
            try {
                // Calculate the delay time
                sleepTime = nextTimeStamp - (new Date()).getTime();
                SilverTrace.info(MODULE_NAME, "SchedulerJob.run", "root.MSG_GEN_PARAM_VALUE",
                        ": Sleeptime = " + sleepTime);
                if (sleepTime < 0) {
                    // Yields if there are problems with the date. Should normaly not
                    // occour.
                    Thread.currentThread().sleep(0);
                } else {
                    // Sleeps up to the next schedule time
                    Thread.currentThread().sleep(sleepTime);
                }
            } catch (InterruptedException aException) {
            }

            if (bRunnable && ((new Date()).getTime() >= nextTimeStamp)) {
                try {
                    SilverTrace.info(MODULE_NAME, "SchedulerJob.run", "root.MSG_GEN_PARAM_VALUE",
                            ": ---------------- Start of job '" + sJobName + "' -------------------");
                    // Execute the functionality of the job and gets a new schedule time
                    nextTimeStamp = getNextTimeStamp();
                    Date now = new Date();
                    JobExecutionContext ctx = JobExecutionContext.createWith(sJobName, now);
                    try {
                        // execute (logStream, new Date ());
                        execute(now);
                        // logStream.flush ();
                        theOwner.jobSucceeded(SchedulerEvent.jobSucceeded(ctx));
                    } catch (SchedulerException aException) {
                        theOwner.jobFailed(SchedulerEvent.jobFailed(ctx, aException));
                    }

                    SilverTrace.info(MODULE_NAME, "SchedulerJob.run", "root.MSG_GEN_PARAM_VALUE",
                            ": ---------------- End of job '" + sJobName + "' -------------------");
                    SilverTrace.info(MODULE_NAME, "SchedulerJob.run", "root.MSG_GEN_PARAM_VALUE",
                            ": Next schedule time: " + logDateFormat.format(new Date(nextTimeStamp)));
                } catch (Exception aException) {
                    SilverTrace.error(MODULE_NAME, "SchedulerJob.run", "root.EX_NO_MESSAGE", aException);
                }
            }
        }
        theOwner = null;
        sJobName = null;
    }

    /**
     * Stops the scheduling of the job
     */
    public synchronized void stop() {
        bRunnable = false;
    }

    /**
     * This method holds the logic of the job. It has to be overwriten in subclasses of this class
     * @param log A PrintStream for text writings in the log file for this job
     * @param theExecutionDate The date of the execution
     * @throws SchedulerException
     */
    abstract protected void execute(Date theExecutionDate) throws SchedulerException;

    /**
     * The constructor has proteceted access, because the generation of jobs should be done in a
     * central way by the class 'SimpleScheduler'
     * @param aController The controller, that controls all job executions
     * @param aOwner The owner of the job
     * @param aJobName The name of the job
     * @param aLogBaseFile The log file for the job
     */
    protected SchedulerJob(SimpleScheduler aController, SchedulerEventListener aOwner, String aJobName)
            throws SchedulerException {
        if (aController == null) {
            throw new SchedulerException("SchedulerJob.SchedulerJob: Parameter 'aController' is null");
        }

        if (aOwner == null) {
            throw new SchedulerException("SchedulerJob.SchedulerJob: Parameter 'aOwner' is null");
        }

        if (aJobName == null) {
            throw new SchedulerException("SchedulerJob.SchedulerJob: Parameter 'aJobName' is null");
        }

        theOwner = aOwner;

        sJobName = aJobName;
        vMinutes = new ArrayList<Integer>();
        vHours = new ArrayList<Integer>();
        vDaysOfMonth = new ArrayList<Integer>();
        vMonths = new ArrayList<Integer>();
        vDaysOfWeek = new ArrayList<Integer>();
        // Instead
        Calendar calInit = Calendar.getInstance();
        currentMinute = 0;
        currentHour = 0;
        if (calInit.getActualMinimum(Calendar.DAY_OF_MONTH) == calInit.get(Calendar.DAY_OF_MONTH)) {
            currentDayOfMonth = calInit.getActualMaximum(Calendar.DAY_OF_MONTH);
            if (calInit.getActualMinimum(Calendar.MONTH) == calInit.get(Calendar.MONTH)) {
                currentMonth = calInit.getActualMaximum(Calendar.MONTH);
                currentYear = calInit.get(Calendar.YEAR) - 1;
            } else {
                currentMonth = calInit.get(Calendar.MONTH) - 1;
                currentYear = calInit.get(Calendar.YEAR);
            }
        } else {
            currentDayOfMonth = calInit.get(Calendar.DAY_OF_MONTH) - 1;
            currentMonth = calInit.get(Calendar.MONTH);
            currentYear = calInit.get(Calendar.YEAR);
        }
        if (calInit.getActualMinimum(Calendar.MONTH) == currentMonth) {
            currentMonth = calInit.getActualMaximum(Calendar.MONTH);
            currentYear = currentYear - 1;
        } else {
            currentMonth = currentMonth - 1;
        }
        nextTimeStamp = 0;
        bRunnable = true;
    }

    /**
     * This method sets the scheduling parameter. The time settings are given by vectors. Each vector
     * holds a list of Integer objects (currently ordered). Every Integer represents a element of a
     * timestamp (cron like).
     * @param startMinutes A list of minutes (0-59)
     * @param startHours A list of hours (0-23)
     * @param startDaysOfMonth A list of days of a month (1-31)
     * @param startMonths A list of months (1-12; starts with 1 for January)
     * @param startDaysOfWeek A list of day of a week (0-6; starts with 0 for Sunday)
     */
    protected synchronized void setSchedulingParameter(List<Integer> startMinutes, List<Integer> startHours,
            List<Integer> startDaysOfMonth, List<Integer> startMonths, List<Integer> startDaysOfWeek)
            throws SchedulerException {
        Enumeration vectorEnumerator;

        List<Integer> workVector;
        int workInt;

        // Check minute values
        if (startMinutes == null) {
            startMinutes = new ArrayList<Integer>();
        }

        for (Integer minute : startMinutes) {
            try {
                workInt = minute;

                if ((workInt < 0) || (workInt > 59)) {
                    throw new SchedulerException("SchedulerMethodJob.setParameter: A minute value is out of range");
                }
            } catch (ClassCastException aException) {
                throw new SchedulerException("SchedulerMethodJob.setParameter: Can't convert a minute value");
            }
        }

        // Check hour values
        if (startHours == null) {
            startHours = new ArrayList<Integer>();
        }

        for (Integer hours : startHours) {
            try {
                workInt = hours;

                if ((workInt < 0) || (workInt > 23)) {
                    throw new SchedulerException("SchedulerMethodJob.setParameter: A hour value is out of range");
                }
            } catch (ClassCastException aException) {
                throw new SchedulerException("SchedulerMethodJob.setParameter: Can't convert a hour value");
            }
        }

        // Check day of month values
        if (startDaysOfMonth == null) {
            startDaysOfMonth = new ArrayList<Integer>();
        }

        for (Integer days : startDaysOfMonth) {
            try {
                workInt = days;

                if ((workInt < 1) || (workInt > 31)) {
                    throw new SchedulerException(
                            "SchedulerMethodJob.setParameter: A day of month value is out of range");
                }
            } catch (ClassCastException aException) {
                throw new SchedulerException("SchedulerMethodJob.setParameter: Can't convert a day of month value");
            }
        }

        // Check month values and normalize them for internal usage
        if (startMonths == null) {
            startMonths = new ArrayList<Integer>();
        }

        workVector = new ArrayList<Integer>();
        for (Integer month : startMonths) {
            try {
                workInt = month;

                if ((workInt < 1) || (workInt > 12)) {
                    throw new SchedulerException("SchedulerMethodJob.setParameter: A month value is out of range");
                }

                workVector.add(workInt - 1); // Internal: zero based
            } catch (ClassCastException aException) {
                throw new SchedulerException("SchedulerMethodJob.setParameter: Can't convert a month value");
            }
        }
        startMonths = workVector;

        // Check day of week values
        if (startDaysOfWeek == null) {
            startDaysOfWeek = new ArrayList<Integer>();
        }

        workVector = new ArrayList<Integer>();
        for (Integer daysOfWeek : startDaysOfWeek) {
            try {
                workInt = daysOfWeek;

                if ((workInt < 0) || (workInt > 6)) {
                    throw new SchedulerException(
                            "SchedulerMethodJob.setParameter: A day of week value is out of range");
                }

                // Conversion not realy necessary, but what if SUN changes the
                // implementation .... :-))
                switch (workInt) {
                case 0:
                    workVector.add(Calendar.SUNDAY);
                    break;
                case 1:
                    workVector.add(Calendar.MONDAY);
                    break;
                case 2:
                    workVector.add(Calendar.TUESDAY);
                    break;
                case 3:
                    workVector.add(Calendar.WEDNESDAY);
                    break;
                case 4:
                    workVector.add(Calendar.THURSDAY);
                    break;
                case 5:
                    workVector.add(Calendar.FRIDAY);
                    break;
                case 6:
                    workVector.add(Calendar.SATURDAY);
                    break;
                }
            } catch (ClassCastException aException) {
                throw new SchedulerException("SchedulerMethodJob.setParameter: Can't convert a day of week value");
            }
        }
        startDaysOfWeek = workVector;

        // Assign the calculated values
        vMinutes = startMinutes;
        vHours = startHours;
        vDaysOfMonth = startDaysOfMonth;
        vMonths = startMonths;
        vDaysOfWeek = startDaysOfWeek;

        // Sort the calculated vectors
        sortCronVectors();
    }

    /**
     * This method sets the scheduling parameter. It is given by a cron like string (currently ranges
     * are not allowed). So the string '* 3,21 * 3 0' starts the execution every Sunday in March on
     * 03:00 and 21:00. The allowed ranges are: minutes (0-59), hours (0-23), days of a month (1-31),
     * months (1-12; starts with 1 for January), day of a week (0-6; starts with 0 for Sunday).
     * Currently the parsing of the cron string ist not done by a state machine but by
     * StringTokenizers so this method is <B>very</B> sensitive for syntax failures!
     * @param aCronString
     */
    protected synchronized void setSchedulingParameter(CronJobTrigger trigger) throws SchedulerException {
        setTrigger(trigger);
        String aCronString = trigger.getCronExpression();
        StringTokenizer fieldSeparator;
        StringTokenizer fieldContentSeparator;
        String workString;
        int workInt;

        if (aCronString == null) {
            throw new SchedulerException("SchedulerShellJob.setCronString: Parameter 'aCronString' is null");
        }
        if (aCronString.contains("-") || aCronString.contains("/")) {
            throw new SchedulerException(
                    "This scheduler backend doesn't support yet the range and the increment instructions "
                            + "in cron expressions");
        }
        aCronString = aCronString.replaceAll("\\?", "*");

        // Reset current values
        vMinutes = new ArrayList<Integer>();
        vHours = new ArrayList<Integer>();
        vDaysOfMonth = new ArrayList<Integer>();
        vMonths = new ArrayList<Integer>();
        vDaysOfWeek = new ArrayList<Integer>();

        // This StringTokenizer splits the cron string into time fields
        fieldSeparator = new StringTokenizer(aCronString);

        // Get minute values
        if (fieldSeparator.hasMoreTokens()) {
            // This StringTokenizer splits each timefield list into single numbers
            fieldContentSeparator = new StringTokenizer(fieldSeparator.nextToken(), ",");
            while (fieldContentSeparator.hasMoreTokens()) {
                workString = fieldContentSeparator.nextToken();

                // Check ingnore token
                if (workString.equals("*")) {
                    vMinutes = new ArrayList<Integer>();
                    break;
                }

                // Check integer value
                try {
                    workInt = Integer.parseInt(workString);
                } catch (NumberFormatException aException) {
                    throw new SchedulerException("SchedulerShellJob.setCronString: Can't convert a minute value");
                }

                if ((workInt < 0) || (workInt > 59)) {
                    throw new SchedulerException("SchedulerShellJob.setCronString: A minute value is out of range");
                }

                vMinutes.add(workInt);
            }
        }

        // Get hour values
        if (fieldSeparator.hasMoreTokens()) {
            fieldContentSeparator = new StringTokenizer(fieldSeparator.nextToken(), ",");
            while (fieldContentSeparator.hasMoreTokens()) {
                workString = fieldContentSeparator.nextToken();

                // Check ingnore token
                if (workString.equals("*")) {
                    vHours = new ArrayList<Integer>();
                    break;
                }

                // Check iteger value
                try {
                    workInt = Integer.parseInt(workString);
                } catch (NumberFormatException aException) {
                    throw new SchedulerException("SchedulerShellJob.setCronString: Can't convert a hour value");
                }

                if ((workInt < 0) || (workInt > 23)) {
                    throw new SchedulerException("SchedulerShellJob.setCronString: A hour value is out of range");
                }

                vHours.add(workInt);
            }
        }

        // Get day of month values and normalize them for internal usage
        if (fieldSeparator.hasMoreTokens()) {
            fieldContentSeparator = new StringTokenizer(fieldSeparator.nextToken(), ",");
            while (fieldContentSeparator.hasMoreTokens()) {
                workString = fieldContentSeparator.nextToken();

                // Check ingnore token
                if (workString.equals("*")) {
                    vDaysOfMonth = new ArrayList<Integer>();
                    break;
                }

                // Check iteger value
                try {
                    workInt = Integer.parseInt(workString);
                } catch (NumberFormatException aException) {
                    throw new SchedulerException(
                            "SchedulerShellJob.setCronString: Can't convert a day of month value");
                }

                if ((workInt < 1) || (workInt > 31)) {
                    throw new SchedulerException(
                            "SchedulerShellJob.setCronString: A day of month value is out of range");
                }

                vDaysOfMonth.add(workInt);
            }
        }

        // Get month values
        if (fieldSeparator.hasMoreTokens()) {
            fieldContentSeparator = new StringTokenizer(fieldSeparator.nextToken(), ",");
            while (fieldContentSeparator.hasMoreTokens()) {
                workString = fieldContentSeparator.nextToken();

                // Check ingnore token
                if (workString.equals("*")) {
                    vMonths = new ArrayList<Integer>();
                    break;
                }

                // Check iteger value
                try {
                    workInt = Integer.parseInt(workString);
                } catch (NumberFormatException aException) {
                    throw new SchedulerException("SchedulerShellJob.setCronString: Can't convert a month value");
                }

                if ((workInt < 1) || (workInt > 12)) {
                    throw new SchedulerException("SchedulerShellJob.setCronString: A month value is out of range");
                }

                vMonths.add(workInt - 1); // Internal: zero based
            }
        }

        // Get day of week values
        if (fieldSeparator.hasMoreTokens()) {
            fieldContentSeparator = new StringTokenizer(fieldSeparator.nextToken(), ",");
            while (fieldContentSeparator.hasMoreTokens()) {
                workString = fieldContentSeparator.nextToken();

                // Check ingnore token
                if (workString.equals("*")) {
                    vDaysOfWeek = new ArrayList<Integer>();
                    break;
                }

                // Check iteger value
                try {
                    workInt = Integer.parseInt(workString);
                } catch (NumberFormatException aException) {
                    throw new SchedulerException(
                            "SchedulerShellJob.setCronString: Can't convert a day of week value");
                }

                if ((workInt < 0) || (workInt > 6)) {
                    throw new SchedulerException(
                            "SchedulerShellJob.setCronString: A day of week value is out of range");
                }

                switch (workInt) {
                case 0:
                    vDaysOfWeek.add(Calendar.SUNDAY);
                    break;
                case 1:
                    vDaysOfWeek.add(Calendar.MONDAY);
                    break;
                case 2:
                    vDaysOfWeek.add(Calendar.TUESDAY);
                    break;
                case 3:
                    vDaysOfWeek.add(Calendar.WEDNESDAY);
                    break;
                case 4:
                    vDaysOfWeek.add(Calendar.THURSDAY);
                    break;
                case 5:
                    vDaysOfWeek.add(Calendar.FRIDAY);
                    break;
                case 6:
                    vDaysOfWeek.add(Calendar.SATURDAY);
                    break;
                }
            }
        }

        if (fieldSeparator.hasMoreTokens()) {
            throw new SchedulerException("SchedulerShellJob.setCronString: Too much time fields in cron string");
        }

        if (vDaysOfWeek == null) {
            throw new SchedulerException("SchedulerShellJob.setCronString: Not enough time fields in cron string");
        }

        // Sort the calculated vectors
        sortCronVectors();
    }

    /**
     * Generates a new timestamp
     * @return
     */
    protected long getNextTimeStamp() {
        Calendar calcCalendar;
        long currentTime;
        boolean validTimeStamp;
        boolean carryMinute;
        boolean carryHour;
        boolean carryDayOfMonth;
        boolean carryMonth;
        boolean firstYearAccess;

        calcCalendar = Calendar.getInstance();

        SilverTrace.debug(MODULE_NAME, "SchedulerJob.getNextTimeStamp",
                "Current TimeStamp: " + logDateFormat.format(new Date(getMillisecondsOfCalendar(calcCalendar))));

        currentTime = getMillisecondsOfCalendar(calcCalendar);

        calcCalendar.set(Calendar.YEAR, currentYear);
        calcCalendar.set(Calendar.MONTH, currentMonth);
        calcCalendar.set(Calendar.DAY_OF_MONTH, currentDayOfMonth);
        calcCalendar.set(Calendar.HOUR_OF_DAY, currentHour);
        calcCalendar.set(Calendar.MINUTE, currentMinute);

        SilverTrace.debug(MODULE_NAME, "SchedulerJob.getNextTimeStamp",
                "Start TimeStamp: " + logDateFormat.format(new Date(getMillisecondsOfCalendar(calcCalendar))));

        // !!!!!! The values must be ordered ascend !!!!!
        validTimeStamp = false;
        carryMinute = false;
        carryHour = false;
        carryDayOfMonth = false;
        carryMonth = false;
        firstYearAccess = true;

        while (!validTimeStamp) {
            // Get new minute
            if (vMinutes.isEmpty()) {
                // Default ('*') -> Hit every minute
                // If the cron setting for minutes is *, we don't have to care about
                // incrementing minutes
                // So do a carryHour
                carryMinute = true;
            } else {
                // Special handling for lists with one element
                if (vMinutes.size() == 1) {
                    currentMinute = vMinutes.get(0);
                    carryMinute = !carryMinute;
                    if (!carryMinute) {
                        carryHour = false;
                        carryDayOfMonth = false;
                        carryMonth = false;
                    }
                } else {
                    int indexOfMinutes = vMinutes.indexOf(currentMinute);
                    if ((indexOfMinutes == -1) || (indexOfMinutes == (vMinutes.size() - 1))) {
                        currentMinute = vMinutes.get(0);
                        carryMinute = true;
                    } else {
                        currentMinute = vMinutes.get(indexOfMinutes + 1);
                        carryMinute = false;
                        carryHour = false;
                        carryDayOfMonth = false;
                        carryMonth = false;
                    }
                }
            }
            calcCalendar.set(Calendar.MINUTE, currentMinute);

            // Get new hour
            if (carryMinute) {
                if (vHours.isEmpty()) // Default ('*') -> Hit every hour
                {
                    int maxHour = calcCalendar.getActualMaximum(Calendar.HOUR_OF_DAY);
                    if (currentHour < maxHour) {
                        currentHour = currentHour + 1;
                        carryHour = false;
                        carryDayOfMonth = false;
                        carryMonth = false;
                    } else {
                        currentHour = calcCalendar.getActualMinimum(Calendar.HOUR_OF_DAY);
                        carryHour = true;
                    }
                } else {
                    // Special handling for lists with one element
                    if (vHours.size() == 1) {
                        currentHour = vHours.get(0);
                        carryHour = !carryHour;
                        if (!carryHour) {
                            carryDayOfMonth = false;
                            carryMonth = false;
                        }
                    } else {
                        int indexOfHours = vHours.indexOf(currentHour);
                        if ((indexOfHours == -1) || (indexOfHours == (vHours.size() - 1))) {
                            currentHour = vHours.get(0);
                            carryHour = true;
                        } else {
                            currentHour = vHours.get(indexOfHours + 1);
                            carryHour = false;
                            carryDayOfMonth = false;
                            carryMonth = false;
                        }
                    }
                }
                calcCalendar.set(Calendar.HOUR_OF_DAY, currentHour);
            }

            // Get new day of month
            if (carryHour) {
                if (vDaysOfMonth.isEmpty()) // Default ('*') -> Hit every month
                {
                    int maxMonth = calcCalendar.getActualMaximum(Calendar.DAY_OF_MONTH);
                    if (currentDayOfMonth < maxMonth) {
                        currentDayOfMonth = currentDayOfMonth + 1;
                        carryDayOfMonth = false;
                        carryMonth = false;
                    } else {
                        currentDayOfMonth = calcCalendar.getActualMinimum(Calendar.DAY_OF_MONTH);
                        carryDayOfMonth = true;
                    }
                } else {
                    // Special handling for lists with one element
                    if (vMinutes.size() == 1) {
                        currentDayOfMonth = vDaysOfMonth.get(0);
                        carryDayOfMonth = !carryDayOfMonth;
                        if (!carryDayOfMonth) {
                            carryMonth = false;
                        }
                    } else {
                        int indexOfMonths = vDaysOfMonth.indexOf(currentDayOfMonth);
                        if ((indexOfMonths == -1) || (indexOfMonths == (vDaysOfMonth.size() - 1))) {
                            currentDayOfMonth = vDaysOfMonth.get(0);
                            carryDayOfMonth = true;
                        } else {
                            currentDayOfMonth = vDaysOfMonth.get(indexOfMonths + 1);
                            carryDayOfMonth = false;
                            carryMonth = false;
                        }
                    }
                }
                calcCalendar.set(Calendar.DAY_OF_MONTH, currentDayOfMonth);
            }

            // Get new month
            if (carryDayOfMonth) {
                if (vMonths.isEmpty()) // Default ('*') -> Hit every month
                {
                    int maxMonth = calcCalendar.getActualMaximum(Calendar.MONTH);
                    if (currentMonth < maxMonth) {
                        currentMonth = currentMonth + 1;
                        carryMonth = false;
                    } else {
                        currentMonth = calcCalendar.getActualMinimum(Calendar.MONTH);
                        carryMonth = true;
                    }
                } else {
                    // Special handling for lists with one element
                    if (vMinutes.size() == 1) {
                        currentMonth = vMonths.get(0);
                        carryMonth = !carryMonth;
                    } else {
                        int indexOfMonths = vMonths.indexOf(currentMonth);
                        if ((indexOfMonths == -1) || (indexOfMonths == (vMonths.size() - 1))) {
                            currentMonth = vMonths.get(0);
                            carryMonth = true;
                        } else {
                            currentMonth = vMonths.get(indexOfMonths + 1);
                            carryMonth = false;
                        }
                    }
                }
                calcCalendar.set(Calendar.MONTH, currentMonth);
            }

            // Get new year
            if (carryMonth) {
                // Prevent Check for the 'ever carry' of one element lists
                if ((!firstYearAccess) || ((currentMinute == 0) && (currentHour == 0) && (currentDayOfMonth == 1)
                        && (currentMonth == 0))) {
                    // Hit every year
                    currentYear = currentYear + 1;
                    calcCalendar.set(Calendar.YEAR, currentYear);
                }

                firstYearAccess = false;
            }

            // If time stamp is greater than the current time check the day of week
            if (getMillisecondsOfCalendar(calcCalendar) > currentTime) {
                // Check eventualy day movement while calculations
                if (calcCalendar.get(Calendar.DAY_OF_MONTH) == currentDayOfMonth) {
                    // Check for correct day of week
                    if (vDaysOfWeek.isEmpty()) {
                        validTimeStamp = true;
                    } else {
                        for (Integer dayOfWeek : vDaysOfWeek) {
                            if (calcCalendar.get(Calendar.DAY_OF_WEEK) == dayOfWeek) {
                                validTimeStamp = true;
                                break;
                            }
                        }
                    }
                }
            }
        } // while (getMillisecondsOfCurrentTimeStamp (calcCalendar) < currentTime)

        SilverTrace.debug(MODULE_NAME, "SchedulerJob.getNextTimeStamp",
                "New TimeStamp: " + logDateFormat.format(new Date(getMillisecondsOfCalendar(calcCalendar))));
        return getMillisecondsOfCalendar(calcCalendar);
    }

    /**
     * Wraps calender date access
     * @param aCalendar
     * @return
     */
    protected long getMillisecondsOfCalendar(Calendar aCalendar) {
        return aCalendar.getTime().getTime();
    }

    /**
     * Sorts the internal cron vectors nad remove doubled entries. This is necessary to calculate the
     * correct schedule time
     */
    private void sortCronVectors() {
        Collections.sort(vMinutes);
        Collections.sort(vHours);
        Collections.sort(vDaysOfMonth);
        Collections.sort(vMonths);
        Collections.sort(vDaysOfWeek);

        removeDoubled(vMinutes);
        removeDoubled(vHours);
        removeDoubled(vDaysOfMonth);
        removeDoubled(vMonths);
        removeDoubled(vDaysOfWeek);
    }

    /**
     * Removes doubled entries (Comparable) , if the list is sorted
     */
    private void removeDoubled(List<? extends Comparable> aList) {
        Comparable currentComparable;

        Comparable lastComparable = null;
        for (Iterator listIterator = aList.iterator(); listIterator.hasNext();) {
            try {
                if (lastComparable == null) {
                    lastComparable = (Comparable) listIterator.next();
                } else {
                    currentComparable = (Comparable) listIterator.next();
                    if (lastComparable.compareTo(currentComparable) == 0) {
                        listIterator.remove();
                    } else {
                        lastComparable = currentComparable;
                    }
                }
            } catch (Exception aException) {
                // Unequal
            }
        }
    }

    @Override
    public String getName() {
        return getJobName();
    }

    @Override
    public void execute(JobExecutionContext context) throws Exception {
        execute(new Date());
    }

    @Override
    public SchedulerEventListener getSchedulerEventListener() {
        return this.getOwner();
    }

    @Override
    public JobTrigger getTrigger() {
        return this.trigger;
    }

    protected void setTrigger(final JobTrigger trigger) {
        this.trigger = trigger;
    }
}