| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.log4j.scheduler; |
| |
| import java.util.List; |
| import java.util.Vector; |
| |
| /** |
| * A simple but still useful implementation of a Scheduler (in memory only). |
| * <p></p> |
| * This implementation will work very well when the number of scheduled job is |
| * small, say less than 100 jobs. If a larger number of events need to be |
| * scheduled, than a better adapted data structure for the jobList can give |
| * improved performance. |
| * |
| * @author Ceki |
| */ |
| public class Scheduler extends Thread { |
| |
| /** |
| * Job list. |
| */ |
| List<ScheduledJobEntry> jobList; |
| /** |
| * If set true, scheduler has or should shut down. |
| */ |
| boolean shutdown = false; |
| |
| /** |
| * Create new instance. |
| */ |
| public Scheduler() { |
| super(); |
| jobList = new Vector<>(); |
| } |
| |
| /** |
| * Find the index of a given job. |
| * |
| * @param job job |
| * @return -1 if the job could not be found. |
| */ |
| int findIndex(final Job job) { |
| int size = jobList.size(); |
| boolean found = false; |
| |
| int i = 0; |
| for (; i < size; i++) { |
| ScheduledJobEntry se = jobList.get(i); |
| if (se.job == job) { |
| found = true; |
| break; |
| } |
| } |
| if (found) { |
| return i; |
| } else { |
| return -1; |
| } |
| } |
| |
| /** |
| * Delete the given job. |
| * |
| * @param job job. |
| * @return true if the job could be deleted, and |
| * false if the job could not be found or if the Scheduler is about to |
| * shutdown in which case deletions are not permitted. |
| */ |
| public synchronized boolean delete(final Job job) { |
| // if already shutdown in the process of shutdown, there is no |
| // need to remove Jobs as they will never be executed. |
| if (shutdown) { |
| return false; |
| } |
| int i = findIndex(job); |
| if (i != -1) { |
| ScheduledJobEntry se = jobList.remove(i); |
| if (se.job != job) { // this should never happen |
| new IllegalStateException("Internal programming error"); |
| } |
| // if the job is the first on the list, |
| // then notify the scheduler thread to schedule a new job |
| if (i == 0) { |
| this.notifyAll(); |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| |
| /** |
| * Schedule a {@link Job} for execution at system time given by |
| * the <code>desiredTime</code> parameter. |
| * |
| * @param job job to schedule. |
| * @param desiredTime desired time of execution. |
| */ |
| public synchronized void schedule(final Job job, |
| final long desiredTime) { |
| schedule(new ScheduledJobEntry(job, desiredTime)); |
| } |
| |
| /** |
| * Schedule a {@link Job} for execution at system time given by |
| * the <code>desiredTime</code> parameter. |
| * <p></p> |
| * The job will be rescheduled. It will execute with a frequency determined |
| * by the period parameter. |
| * |
| * @param job job to schedule. |
| * @param desiredTime desired time of execution. |
| * @param period repeat period. |
| */ |
| public synchronized void schedule(final Job job, |
| final long desiredTime, |
| final long period) { |
| schedule(new ScheduledJobEntry(job, desiredTime, period)); |
| } |
| |
| /** |
| * Change the period of a job. The original job must exist for its period |
| * to be changed. |
| * <p></p> |
| * The method returns true if the period could be changed, and false |
| * otherwise. |
| * |
| * @param job job. |
| * @param newPeriod new repeat period. |
| * @return true if period could be changed. |
| */ |
| public synchronized boolean changePeriod(final Job job, |
| final long newPeriod) { |
| if (newPeriod <= 0) { |
| throw new IllegalArgumentException( |
| "Period must be an integer langer than zero"); |
| } |
| |
| int i = findIndex(job); |
| if (i == -1) { |
| return false; |
| } else { |
| ScheduledJobEntry se = jobList.get(i); |
| se.period = newPeriod; |
| return true; |
| } |
| } |
| |
| /** |
| * Schedule a job. |
| * |
| * @param newSJE new job entry. |
| */ |
| private synchronized void schedule(final ScheduledJobEntry newSJE) { |
| // disallow new jobs after shutdown |
| if (shutdown) { |
| return; |
| } |
| int max = jobList.size(); |
| long desiredExecutionTime = newSJE.desiredExecutionTime; |
| |
| // find the index i such that timeInMillis < jobList[i] |
| int i = 0; |
| for (; i < max; i++) { |
| |
| ScheduledJobEntry sje = jobList.get(i); |
| |
| if (desiredExecutionTime < sje.desiredExecutionTime) { |
| break; |
| } |
| } |
| jobList.add(i, newSJE); |
| // if the jobList was empty, then notify the scheduler thread |
| if (i == 0) { |
| this.notifyAll(); |
| } |
| } |
| |
| /** |
| * Shut down scheduler. |
| */ |
| public synchronized void shutdown() { |
| shutdown = true; |
| } |
| |
| /** |
| * Run scheduler. |
| */ |
| public synchronized void run() { |
| while (!shutdown) { |
| if (jobList.isEmpty()) { |
| linger(); |
| } else { |
| ScheduledJobEntry sje = jobList.get(0); |
| long now = System.currentTimeMillis(); |
| if (now >= sje.desiredExecutionTime) { |
| executeInABox(sje.job); |
| jobList.remove(0); |
| if (sje.period > 0) { |
| sje.desiredExecutionTime = now + sje.period; |
| schedule(sje); |
| } |
| } else { |
| linger(sje.desiredExecutionTime - now); |
| } |
| } |
| } |
| // clear out the job list to facilitate garbage collection |
| jobList.clear(); |
| jobList = null; |
| System.out.println("Leaving scheduler run method"); |
| } |
| |
| /** |
| * We do not want a single failure to affect the whole scheduler. |
| * |
| * @param job job to execute. |
| */ |
| void executeInABox(final Job job) { |
| try { |
| job.execute(); |
| } catch (Exception e) { |
| System.err.println("The execution of the job threw an exception"); |
| e.printStackTrace(System.err); |
| } |
| } |
| |
| /** |
| * Wait for notification. |
| */ |
| void linger() { |
| try { |
| while (jobList.isEmpty() && !shutdown) { |
| this.wait(); |
| } |
| } catch (InterruptedException ie) { |
| shutdown = true; |
| } |
| } |
| |
| /** |
| * Wait for notification or time to elapse. |
| * |
| * @param timeToLinger time to linger. |
| */ |
| void linger(final long timeToLinger) { |
| try { |
| this.wait(timeToLinger); |
| } catch (InterruptedException ie) { |
| shutdown = true; |
| } |
| } |
| |
| /** |
| * Represents an entry in job scheduler. |
| */ |
| static final class ScheduledJobEntry { |
| /** |
| * Desired execution time. |
| */ |
| long desiredExecutionTime; |
| /** |
| * Job to run. |
| */ |
| Job job; |
| /** |
| * Repeat period. |
| */ |
| long period = 0; |
| |
| /** |
| * Create new instance. |
| * |
| * @param job job |
| * @param desiredTime desired time. |
| */ |
| ScheduledJobEntry(final Job job, final long desiredTime) { |
| this(job, desiredTime, 0); |
| } |
| |
| /** |
| * Create new instance. |
| * |
| * @param job job |
| * @param desiredTime desired time |
| * @param period repeat period |
| */ |
| ScheduledJobEntry(final Job job, |
| final long desiredTime, |
| final long period) { |
| super(); |
| this.desiredExecutionTime = desiredTime; |
| this.job = job; |
| this.period = period; |
| } |
| } |
| |
| } |
| |
| |