// Copyright (c) 2001 Dustin Sallings <dustin@spy.net> package net.spy.cron; import java.io.File; import java.util.Calendar; import java.util.Date; import java.util.concurrent.LinkedBlockingQueue; import net.spy.SpyThread; import net.spy.concurrent.ThreadPool; /** * Watches a JobQueue and invokes the Jobs when they're ready. */ public final class Cron extends SpyThread { private final JobQueue<?> jobQueue; private volatile boolean stillRunning=true; private ThreadPool threads=null; // How long we can go idle. private long maxIdleTime=900000; // Time we last saw a valid job private long validJobFound=0; /** * Get a new Cron instance operating on the given queue. * * @param jq the job queue to watch */ public Cron(JobQueue<?> jq) { this("Cron", jq); } /** * Get a new Cron instance with a name, JobQueue and the default thread * pool. * * @param name name of the cron instance * @param jq the queue to watch */ public Cron(String name, JobQueue<?> jq) { this(name, jq, null); } /** * Get a new Cron object operating on the given queue. * * @param name thread name * @param jq job queue to watch * @param tp the thread pool */ public Cron(String name, JobQueue<?> jq, ThreadPool tp) { super(new ThreadGroup(name), name); this.jobQueue=jq; setDaemon(true); // Set the thread group to a daemon getThreadGroup().setDaemon(true); threads=tp; if(threads==null) { threads=new ThreadPool(name + "Pool", 1, 10, Thread.NORM_PRIORITY, new LinkedBlockingQueue<Runnable>(128)); } validJobFound=System.currentTimeMillis(); start(); } /** * String me. */ @Override public String toString() { String extra=null; if(jobQueue==null) { extra = " - null jobqueue"; } else { extra = " - watching " + jobQueue.size() + " jobs, next up at " + jobQueue.getNextStartDate(); } return(super.toString() + extra); } /** * Get the current job queue. */ public JobQueue<?> getJobQueue() { return(jobQueue); } /** * Shut down the queue. */ public void shutdown() { if(!isRunning()) { throw new IllegalStateException("Already shut down"); } stillRunning=false; threads.shutdown(); // XXX: Write up why I did this. synchronized(jobQueue) { jobQueue.notifyAll(); } } /** * True if this Cron instance is still running. */ public boolean isRunning() { return(this.stillRunning); } /** * Do the run thing. */ @Override public void run() { getLogger().info("Starting cron services"); while(stillRunning) { // Check all the running jobs. for(Job j : jobQueue.getReadyJobs()) { getLogger().info("Starting job " + j); threads.addTask(j); } // Find the soonest job less than a day out. long now=System.currentTimeMillis(); Date next=jobQueue.getNextStartDate(); long soonestJob=0; // If we didn't get a next job start date, the queue is likely // empty. If we shut down on an empty queue, shut down. if(next==null) { getLogger().debug("No job in queue"); // If it's been too long, shut down if( (now-validJobFound) > maxIdleTime) { getLogger().info("Been a long time " + "since I had a job. Shutting down."); getLogger().debug("now: %s", now); getLogger().debug("validJobFound: %s", validJobFound); getLogger().debug("maxIdleTime: %s", maxIdleTime); shutdown(); } soonestJob=60000; } else { soonestJob=next.getTime()-now; validJobFound=now; } try { if(next!=null) { getLogger().debug("Sleeping %sms (next job at %s).", soonestJob, next); } else { getLogger().debug("Sleeping %sms (no good date found).", soonestJob); } // If we're still running at this point, wait for a job if(stillRunning) { // Take a second off of the sleep (will use it later) soonestJob-=1000; // Make sure it's greater than 1, or it'll sleep forever if(soonestJob < 1) { soonestJob = 1; } getLogger().debug("Sleeping for " + soonestJob); // Sleep on the job synchronized(jobQueue) { jobQueue.wait(soonestJob); } // Wait the remaining second sleep(1000); getLogger().debug("Finished sleep."); } else { getLogger().debug("Not sleeping, game over."); } } catch(Exception e) { getLogger().warn("Exception in cron loop", e); } } // still running getLogger().info("shut down at"); } /** * Set the maximum amount of time the cron thread will continue running * with no jobs. * * @param to maximum amount of time in milliseconds */ public void setMaxIdleTime(long to) { this.maxIdleTime=to; } /** * Run a Cron instance against a FileJobQueue. */ public static void main(String args[]) throws Exception { FileJobQueue jq=new FileJobQueue(new File(args[0])); Cron c=new Cron(jq); // Get the time incrementor. TimeIncrement ti=new TimeIncrement(); ti.setField(Calendar.MINUTE); ti.setIncrement(2); while(c.isAlive()) { sleep(10000); } } }