001package com.hfg.util.scheduler;
002
003import java.util.Date;
004import java.util.logging.Level;
005import java.util.logging.Logger;
006
007import com.hfg.util.StringUtil;
008
009//------------------------------------------------------------------------------
010/**
011 Runs specified job on a specified schedule.
012 <div>
013 @author J. Alex Taylor, hairyfatguy.com
014 </div>
015 */
016//------------------------------------------------------------------------------
017// com.hfg Library
018//
019// This library is free software; you can redistribute it and/or
020// modify it under the terms of the GNU Lesser General Public
021// License as published by the Free Software Foundation; either
022// version 2.1 of the License, or (at your option) any later version.
023//
024// This library is distributed in the hope that it will be useful,
025// but WITHOUT ANY WARRANTY; without even the implied warranty of
026// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
027// Lesser General Public License for more details.
028//
029// You should have received a copy of the GNU Lesser General Public
030// License along with this library; if not, write to the Free Software
031// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
032//
033// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
034// jataylor@hairyfatguy.com
035//------------------------------------------------------------------------------
036
037public class ScheduledJob implements Runnable
038{
039   private String   mName;
040   private Runnable mJob;
041   private Schedule mSchedule;
042   private Thread   mThread;
043   private boolean  mIsActive = true; // Should this job still be managed by the Scheduler
044   private boolean  mIsPaused = false;
045   private boolean  mIsCurrentlyExecuting = false;
046   private boolean  mExecuteNow = false;
047   private Date     mLastExecutionStartDate;
048
049   private final static Logger LOGGER = Logger.getLogger(Scheduler.class.getPackage().getName());
050
051   //###########################################################################
052   // CONSTRUCTORS
053   //###########################################################################
054
055   //------------------------------------------------------------------------
056   public ScheduledJob(Runnable inJob, Schedule inSchedule)
057   {
058      mJob      = inJob;
059      mSchedule = inSchedule;
060   }
061
062   //###########################################################################
063   // PUBLIC METHODS
064   //###########################################################################
065
066   //------------------------------------------------------------------------
067   public ScheduledJob setName(String inValue)
068   {
069      mName = inValue;
070      return this;
071   }
072
073   //------------------------------------------------------------------------
074   public String name()
075   {
076      return (StringUtil.isSet(mName) ? mName : mJob.getClass().getSimpleName());
077   }
078
079   //------------------------------------------------------------------------
080   @Override
081   public String toString()
082   {
083      return name();
084   }
085
086   //------------------------------------------------------------------------
087   public Runnable getJob()
088   {
089      return mJob;
090   }
091
092   //------------------------------------------------------------------------
093   public Schedule getSchedule()
094   {
095      return mSchedule;
096   }
097
098   //------------------------------------------------------------------------
099   public Thread getThread()
100   {
101      return mThread;
102   }
103
104   //------------------------------------------------------------------------
105   public Date getLastExecutionStartDate()
106   {
107      return mLastExecutionStartDate;
108   }
109
110   //------------------------------------------------------------------------
111   public void start()
112   {
113      if (null == mThread)
114      {
115         mThread = new Thread(this);
116         mThread.setName(name());
117         mThread.start();
118      }
119
120      mIsActive = true;
121   }
122
123   //------------------------------------------------------------------------
124   public void stop()
125   {
126      mIsActive = false;
127      mThread.interrupt();
128   }
129
130   //------------------------------------------------------------------------
131   public void pause()
132   {
133      mIsPaused = true;
134   }
135
136   //------------------------------------------------------------------------
137   public void resume()
138   {
139      mIsPaused = false;
140   }
141
142   //------------------------------------------------------------------------
143   public void executeNow()
144   {
145      if (! mIsCurrentlyExecuting)
146      {
147         mExecuteNow = true;
148         mThread.interrupt();
149      }
150   }
151
152   //------------------------------------------------------------------------
153   public void run()
154   {
155      while (mIsActive)
156      {
157         try
158         {
159            Date nextScheduledExecutionTime = mSchedule.next();
160            if (nextScheduledExecutionTime != null)
161            {
162               if (! mExecuteNow)
163               {
164                  LOGGER.log(Level.FINE, "Next scheduled execution time: " + nextScheduledExecutionTime);
165
166                  // Just sleep until it is time to execute this job again
167                  Thread.sleep(nextScheduledExecutionTime.getTime() - System.currentTimeMillis());
168
169               }
170
171               if (mIsPaused
172                     && ! mExecuteNow)
173               {
174                  LOGGER.log(Level.FINE, "Skipping this execution instance since this job is currently paused.");
175               }
176               else
177               {
178                  mExecuteNow = false;
179                  mIsCurrentlyExecuting = true;
180                  mLastExecutionStartDate = new Date();
181
182                  mJob.run();
183               }
184            }
185         }
186         catch (InterruptedException e)
187         {
188            // Ignore
189         }
190         catch (Throwable e2)
191         {
192            e2.printStackTrace();
193            //TODO: Have the scheduler log the exception
194         }
195      }
196
197      LOGGER.log(Level.INFO, "Scheduled job " + mJob.getClass().getSimpleName() + " is exiting and will no longer be run.");
198   }
199
200}