001package org.apache.turbine.services.schedule;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.Calendar;
023import java.util.Date;
024
025import org.apache.commons.lang3.StringUtils;
026import org.apache.logging.log4j.LogManager;
027import org.apache.logging.log4j.Logger;
028import org.apache.turbine.util.TurbineException;
029
030/**
031 * This class provides the basic implementation of common features for a scheduled job entry.
032 *
033 * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
034 */
035public abstract class AbstractJobEntry implements JobEntry
036{
037    /** Logging */
038    protected static final Logger log = LogManager.getLogger(ScheduleService.LOGGER_NAME);
039
040    /** indicates if job is currently running */
041    private boolean jobIsActive = false;
042
043    /** Next runtime. **/
044    private long runtime = 0;
045
046    /** schedule types **/
047    protected enum ScheduleType {
048        SECOND,
049        MINUTE,
050        WEEK_DAY,
051        DAY_OF_MONTH,
052        DAILY
053    }
054
055    /**
056     * Default constructor
057     */
058    public AbstractJobEntry()
059    {
060        super();
061    }
062
063    /**
064     * Constructor.
065     *
066     * Schedule a job to run on a certain point of time.<br>
067     *
068     * Example 1: Run the DefaultScheduledJob at 8:00am every 15th of
069     * the month - <br>
070     *
071     * JobEntry je = new JobEntry(0,0,8,-1,15,"DefaultScheduledJob");<br>
072     *
073     * Example 2: Run the DefaultScheduledJob at 8:00am every day -
074     * <br>
075     *
076     * JobEntry je = new JobEntry(0,0,8,-1,-1,"DefaultScheduledJob");<br>
077     *
078     * Example 3: Run the DefaultScheduledJob every 2 hours. - <br>
079     *
080     * JobEntry je = new JobEntry(0,120,-1,-1,-1,"DefaultScheduledJob");<br>
081     *
082     * Example 4: Run the DefaultScheduledJob every 30 seconds. - <br>
083     *
084     * JobEntry je = new JobEntry(30,-1,-1,-1,-1,"DefaultScheduledJob");<br>
085     *
086     * @param sec Value for entry "seconds".
087     * @param min Value for entry "minutes".
088     * @param hour Value for entry "hours".
089     * @param wd Value for entry "week days".
090     * @param day_mo Value for entry "month days".
091     * @param task Task to execute.
092     * @throws TurbineException a generic exception.
093     */
094    public AbstractJobEntry(int sec,
095                    int min,
096                    int hour,
097                    int wd,
098                    int day_mo,
099                    String task)
100            throws TurbineException
101    {
102        this();
103
104        if (StringUtils.isEmpty(task))
105        {
106            throw new TurbineException("Error in JobEntry. " +
107                    "Bad Job parameter. Task not set.");
108        }
109
110        setSecond(sec);
111        setMinute(min);
112        setHour(hour);
113        setWeekDay(wd);
114        setDayOfMonth(day_mo);
115        setTask(task);
116
117        calcRunTime();
118    }
119
120    /**
121     * Used for ordering Jobentries
122     * Note: this comparator imposes orderings that are inconsistent with
123     * equals.
124     *
125     * @param je The first <code>JobEntry</code> object.
126     * @return An <code>int</code> indicating the result of the comparison.
127     */
128    @Override
129    public int compareTo(JobEntry je)
130    {
131        return getJobId() - je.getJobId();
132    }
133
134    /**
135     * Sets whether the job is running.
136     *
137     * @param isActive Whether the job is running.
138     */
139    @Override
140    public void setActive(boolean isActive)
141    {
142        jobIsActive = isActive;
143    }
144
145    /**
146     * Check to see if job is currently active/running
147     *
148     * @return true if job is currently queuing run by the
149     *  worker thread, otherwise false
150     */
151    @Override
152    public boolean isActive()
153    {
154        return jobIsActive;
155    }
156
157    /**
158     * Get the next runtime for this job as a long.
159     *
160     * @return The next run time as a long.
161     */
162    @Override
163    public long getNextRuntime()
164    {
165        return runtime;
166    }
167
168    /**
169     * Gets the next runtime as a date
170     *
171     * @return Next run date
172     */
173    @Override
174    public Date getNextRunDate()
175    {
176        return new Date(runtime);
177    }
178
179    /**
180     * Get the next runtime for this job as a String.
181     *
182     * @return The next run time as a String.
183     */
184    @Override
185    public String getNextRunAsString()
186    {
187        return getNextRunDate().toString();
188    }
189
190    /**
191     * Calculate how long before the next runtime.<br>
192     *
193     * The runtime determines it's position in the job queue.
194     * Here's the logic:<br>
195     *
196     * 1. Create a date the represents when this job is to run.<br>
197     *
198     * 2. If this date has expired, them "roll" appropriate date
199     * fields forward to the next date.<br>
200     *
201     * 3. Calculate the diff in time between the current time and the
202     * next run time.<br>
203     *
204     * @throws TurbineException a generic exception.
205     */
206    @Override
207    public void calcRunTime()
208            throws TurbineException
209    {
210        Calendar schedrun = Calendar.getInstance();
211        Calendar now = Calendar.getInstance();
212
213        switch (evaluateJobType())
214        {
215            case SECOND:
216                // SECOND (every so many seconds...)
217                schedrun.add(Calendar.SECOND, getSecond());
218                runtime = schedrun.getTime().getTime();
219                break;
220
221            case MINUTE:
222                // MINUTE (every so many minutes...)
223                schedrun.add(Calendar.SECOND, getSecond());
224                schedrun.add(Calendar.MINUTE, getMinute());
225                runtime = schedrun.getTime().getTime();
226                break;
227
228            case WEEK_DAY:
229                // WEEKDAY (day of the week)
230                schedrun.set(Calendar.SECOND, getSecond());
231                schedrun.set(Calendar.MINUTE, getMinute());
232                schedrun.set(Calendar.HOUR_OF_DAY, getHour());
233                schedrun.set(Calendar.DAY_OF_WEEK, getWeekDay());
234
235                if (now.before(schedrun))
236                {
237                    // Scheduled time has NOT expired.
238                    runtime = schedrun.getTime().getTime();
239                }
240                else
241                {
242                    // Scheduled time has expired; roll to the next week.
243                    schedrun.add(Calendar.DAY_OF_WEEK, 7);
244                    runtime = schedrun.getTime().getTime();
245                }
246                break;
247
248            case DAY_OF_MONTH:
249                // DAY_OF_MONTH (date of the month)
250                schedrun.set(Calendar.SECOND, getSecond());
251                schedrun.set(Calendar.MINUTE, getMinute());
252                schedrun.set(Calendar.HOUR_OF_DAY, getHour());
253                schedrun.set(Calendar.DAY_OF_MONTH, getDayOfMonth());
254
255                if (now.before(schedrun))
256                {
257                    // Scheduled time has NOT expired.
258                    runtime = schedrun.getTime().getTime();
259                }
260                else
261                {
262                    // Scheduled time has expired; roll to the next month.
263                    schedrun.add(Calendar.MONTH, 1);
264                    runtime = schedrun.getTime().getTime();
265                }
266                break;
267
268            case DAILY:
269                // DAILY (certain hour:minutes of the day)
270                schedrun.set(Calendar.SECOND, getSecond());
271                schedrun.set(Calendar.MINUTE, getMinute());
272                schedrun.set(Calendar.HOUR_OF_DAY, getHour());
273
274                // Scheduled time has NOT expired.
275                if (now.before(schedrun))
276                {
277                    runtime = schedrun.getTime().getTime();
278                }
279                else
280                {
281                    // Scheduled time has expired; roll forward 24 hours.
282                    schedrun.add(Calendar.HOUR_OF_DAY, 24);
283                    runtime = schedrun.getTime().getTime();
284                }
285                break;
286
287            default:
288                // Do nothing.
289        }
290
291        log.info("Next runtime for task {} is {}", this::getTask, this::getNextRunDate);
292    }
293
294    /**
295     * What schedule am I on?
296     *
297     * I know this is kinda ugly!  If you can think of a cleaner way
298     * to do this, please jump in!
299     *
300     * @return A number specifying the type of schedule. See
301     * calcRunTime().
302     * @throws TurbineException a generic exception.
303     */
304    private ScheduleType evaluateJobType()
305            throws TurbineException
306    {
307
308        // First start by checking if it's a day of the month job.
309        if (getDayOfMonth() < 0)
310        {
311            // Not a day of the month job... check weekday.
312            if (getWeekDay() < 0)
313            {
314                // Not a weekday job...check if by the hour.
315                if (getHour() < 0)
316                {
317                    // Not an hourly job...check if it is by the minute
318                    if (getMinute() < 0)
319                    {
320                        // Not a by the minute job so must be by the second
321                        if (getSecond() < 0)
322                        {
323                            throw new TurbineException("Error in JobEntry. Bad Job parameter.");
324                        }
325
326                        return ScheduleType.SECOND;
327                    }
328                    else
329                    {
330                        // Must be a job run by the minute so we need minutes and
331                        // seconds.
332                        if (getMinute() < 0 || getSecond() < 0)
333                        {
334                            throw new TurbineException("Error in JobEntry. Bad Job parameter.");
335                        }
336
337                        return ScheduleType.MINUTE;
338                    }
339                }
340                else
341                {
342                    // Must be a daily job by hours minutes, and seconds.  In
343                    // this case, we need the minute, second, and hour params.
344                    if (getMinute() < 0 || getHour() < 0 || getSecond() < 0)
345                    {
346                        throw new TurbineException("Error in JobEntry. Bad Job parameter.");
347                    }
348
349                    return ScheduleType.DAILY;
350                }
351            }
352            else
353            {
354                // Must be a weekday job.  In this case, we need
355                // minute, second, and hour params
356                if (getMinute() < 0 || getHour() < 0 || getSecond() < 0)
357                {
358                    throw new TurbineException("Error in JobEntry. Bad Job parameter.");
359                }
360
361                return ScheduleType.WEEK_DAY;
362            }
363        }
364        else
365        {
366            // Must be a day of the month job.  In this case, we need
367            // minute, second, and hour params
368            if (getMinute() < 0 || getHour() < 0)
369            {
370                throw new TurbineException("Error in JobEntry. Bad Job parameter.");
371            }
372
373            return ScheduleType.DAY_OF_MONTH;
374        }
375    }
376
377    /**
378     * Get the value of jobId.
379     *
380     * @return int
381     */
382    @Override
383    public abstract int getJobId();
384
385    /**
386     * Set the value of jobId.
387     *
388     * @param v new value
389     */
390    @Override
391    public abstract void setJobId(int v);
392
393    /**
394     * Get the value of second.
395     *
396     * @return int
397     */
398    public abstract int getSecond();
399
400    /**
401     * Set the value of second.
402     *
403     * @param v new value
404     */
405    public abstract void setSecond(int v);
406
407    /**
408     * Get the value of minute.
409     *
410     * @return int
411     */
412    public abstract int getMinute();
413
414    /**
415     * Set the value of minute.
416     *
417     * @param v new value
418     */
419    public abstract void setMinute(int v);
420
421    /**
422     * Get the value of hour.
423     *
424     * @return int
425     */
426    public abstract int getHour();
427
428    /**
429     * Set the value of hour.
430     *
431     * @param v new value
432     */
433    public abstract void setHour(int v);
434
435    /**
436     * Get the value of weekDay.
437     *
438     * @return int
439     */
440    public abstract int getWeekDay();
441
442    /**
443     * Set the value of weekDay.
444     *
445     * @param v new value
446     */
447    public abstract void setWeekDay(int v);
448
449    /**
450     * Get the value of dayOfMonth.
451     *
452     * @return int
453     */
454    public abstract int getDayOfMonth();
455
456    /**
457     * Set the value of dayOfMonth.
458     *
459     * @param v new value
460     */
461    public abstract void setDayOfMonth(int v);
462
463    /**
464     * Get the value of task.
465     *
466     * @return String
467     */
468    @Override
469    public abstract String getTask();
470
471    /**
472     * Set the value of task.
473     *
474     * @param v new value
475     */
476    @Override
477    public abstract void setTask(String v);
478}