org.hyperic.util.schedule.Schedule.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.util.schedule.Schedule.java

Source

/*
 * NOTE: This copyright does *not* cover user programs that use HQ
 * program services by normal system calls through the application
 * program interfaces provided as part of the Hyperic Plug-in Development
 * Kit or the Hyperic Client Development Kit - this is merely considered
 * normal use of the program, and does *not* fall under the heading of
 * "derived work".
 * 
 * Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
 * This file is part of HQ.
 * 
 * HQ is free software; you can redistribute it and/or modify
 * it under the terms version 2 of the GNU General Public License as
 * published by the Free Software Foundation. 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 General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 */

package org.hyperic.util.schedule;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import java.text.DateFormat;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * A generic scheduler object which keeps track of events, when they should
 * be executed, deletion after invocation, etc.  The basetime used when
 * doing any arithmetic with times is the epoch.  The scheduler is 
 * synchronized.
 *
 * Scheduled events have an interval property -- how often the event should 
 * execute (in milliseconds).  
 */

public class Schedule {

    private final Object _lock = new Object();

    private long scheduleID; // Used for assigning unique event IDs
    private Vector schedule; // The actual events being scheduled, sorted
                             // by ascending nextTime in the item

    private Log log = LogFactory.getLog(Schedule.class);

    public Schedule() {
        this.schedule = new Vector();
        this.scheduleID = 0;
    }

    /**
     * Get the next global schedule identifier, and internally increment it.
     *
     * @return a globally unique identifier, for the next scheduled item.
     */

    private long consumeNextGlobalID() {
        synchronized (_lock) {
            return this.scheduleID++;
        }
    }

    /**
     * Insert a pre-made ScheduledItem into the schedule.  The item should
     * already have all appropriate attributes assigned (including the ID).
     *
     * @param item The item to be inserted into the schedule
     */

    private void insertScheduledItem(ScheduledItem item) {
        int i, size = this.schedule.size();
        long nextTime = item.getNextTime();
        ScheduledItem x;

        for (i = 0; i < size; i++) {
            x = (ScheduledItem) this.schedule.get(i);
            if (x.getNextTime() > nextTime) {
                this.schedule.add(i, item);
                return;
            }
        }
        // Else add at the end of the vector (time greater than all others)
        this.schedule.add(item);
    }

    /**
     * Add an item to the internal schedule.  
     *
     * @param item the object to schedule
     * @param interval the number of seconds between invocations of the item
     * @param prev true if the item should be scheduled in the past to
     *        force immediate firing.
     * @param repeat true if the item should stay in the schedule even after
     *               its time has expired
     * @throws UnscheduledItemException If the given schedule interval is <= 0
     *
     * @return a global identifier for the scheduled item
     */

    public synchronized long scheduleItem(Object item, long interval, boolean prev, boolean repeat)
            throws ScheduleException {
        long itemId;
        ScheduledItem newItem;

        if (interval <= 0) {
            throw new ScheduleException("Invalid schedule interval given (" + interval + ")");
        }

        itemId = this.consumeNextGlobalID();
        newItem = new ScheduledItem(item, interval, prev, repeat, itemId);
        this.insertScheduledItem(newItem);
        return itemId;
    }

    /**
     * Add an item to the internal schedule.  
     *
     * @param item the object to schedule
     * @param interval the number of seconds between invocations of the item
     * @param repeat true if the item should stay in the schedule even after
     *               its time has expired
     *
     * @return a global identifier for the scheduled item
     */

    public synchronized long scheduleItem(Object item, long interval, boolean repeat) throws ScheduleException {
        return this.scheduleItem(item, interval, false, repeat);
    }

    /**
     * Add an item to the internal schedule, with the repeat flag set to true.
     * See the documentation for scheduleItem for more information.
     */

    public long scheduleItem(Object item, long interval) throws ScheduleException {
        return this.scheduleItem(item, interval, true);
    }

    /**
     * Remove an item from the schedule.  Scheduled items which have not
     * been consumed may be unscheduled by using this method.
     *
     * @param id ID returned by a call to scheduleItem of the item to remove
     *
     * @throws UnscheduledItemException indicating the ID was not found.
     */

    public synchronized ScheduledItem unscheduleItem(long id) throws UnscheduledItemException {
        int i, size = this.schedule.size();

        for (i = 0; i < size; i++) {
            ScheduledItem item = (ScheduledItem) this.schedule.get(i);

            if (item.getId() == id) {
                if (log.isDebugEnabled()) {
                    log.debug("unscheduling " + item.getObj() + " getNextTime " + getDateStr(item.getNextTime()));
                }
                return (ScheduledItem) this.schedule.remove(i);
            }
        }

        throw new UnscheduledItemException("id '" + id + "' not found");
    }

    /**
     * Get the time that the next scheduled item is to be executed.  The
     * returned time is in UTC since the epoch (similar to 
     * System.currentTimeMillis())
     *
     * @return the absolute the the next event is to be executed
     * 
     * @throws EmptyScheduleException indicating there is no next item for
     *                                which the time can be retrieved.
     */

    public synchronized long getTimeOfNext() throws EmptyScheduleException {
        int size = this.schedule.size();
        ScheduledItem item;

        if (size == 0)
            throw new EmptyScheduleException();

        item = (ScheduledItem) this.schedule.get(0);
        return item.getNextTime();
    }

    /**
     * Get the next item (or items) to be executed.  If more than one item
     * is scheduled for a specific time, they are all returned.  Items 
     * returned by this function are re-inserted into the schedule, if their
     * repeat flag is set to true -- otherwise they are removed.
     *
     * @return a list of items to execute
     *
     * @throws EmptyScheduleException indicating there was no 'next item'
     */

    public synchronized List consumeNextItems() throws EmptyScheduleException {
        int size = this.schedule.size();
        ScheduledItem base;
        ArrayList res;
        long baseNextTime;

        if (size == 0)
            throw new EmptyScheduleException();

        res = new ArrayList(1);

        // We always add the first item to the list of returned objects
        base = (ScheduledItem) this.schedule.get(0);
        baseNextTime = System.currentTimeMillis();
        res.add(base);

        boolean debug = log.isDebugEnabled();
        // Now add other items if they occur at the same time 
        for (int i = 1; i < size; i++) {
            ScheduledItem other = (ScheduledItem) this.schedule.get(i);
            if (debug) {
                log.debug("checking " + other.getObj() + " baseNextTime: " + getDateStr(baseNextTime)
                        + ", getNextTime: " + getDateStr(other.getNextTime()));
            }

            if (other.getNextTime() <= baseNextTime) {
                res.add(other);
            } else {
                break;
            }
        }

        // Finally, loop through the objects we are about to return, so
        // we can re-order our innards, and return the actual objects
        // stored instead of the ScheduledItem
        // XXX -- This could be MUCH more efficient, especially with respect
        //        to re-inserting into the list, since all of the returned
        //        objects are of the same size.
        for (int i = 0; i < res.size(); i++) {
            ScheduledItem other = (ScheduledItem) res.get(i);

            if (debug) {
                log.debug("removing " + other.getObj());
            }
            this.schedule.remove(other);
            if (other.isRepeat()) {
                other.stepNextTime();
                if (debug) {
                    log.debug("adding " + other.getObj() + " getNextTime " + getDateStr(other.getNextTime()));
                }
                this.insertScheduledItem(other);
            }

            res.set(i, other.getObj());
        }
        return res;
    }

    private static String getDateStr(long timems) {
        return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
                .format(new java.util.Date(timems));
    }

    /**
     * Get the number of items in the schedule.
     *
     * @return the number of items in the schedule.
     */

    public int getNumItems() {
        return this.schedule.size();
    }

    /**
     * Get a list of all the currently scheduled items.
     *
     * @return the list of scheduled items.
     */

    public ScheduledItem[] getScheduledItems() {
        return (ScheduledItem[]) this.schedule.toArray(new ScheduledItem[0]);
    }

    public static void main(String args[]) throws Exception {
        Schedule s = new Schedule();

        s.scheduleItem(new Integer(30), 30 * 1000, true);
        s.scheduleItem(new Integer(5), 5 * 1000, true);
        long id_for_8 = s.scheduleItem(new Integer(8), 8 * 1000, true);
        s.scheduleItem(new Integer(10), 10 * 1000, false);
        int secondsRunning = 0;

        while (true) {
            System.out.println(System.currentTimeMillis() / 1000);
            if (System.currentTimeMillis() > s.getTimeOfNext()) {
                List items = s.consumeNextItems();

                System.out.println("Consuming");
                for (int i = 0; i < items.size(); i++) {
                    System.out.println("Consumed: " + (Integer) items.get(i));
                }
            }
            Thread.sleep(1000);
            secondsRunning++;
            if (secondsRunning > 10 && id_for_8 != -1) {
                s.unscheduleItem(id_for_8);
                System.out.println("Removing 8");
                id_for_8 = -1;
            }
            if (secondsRunning > 20)
                System.exit(0);
        }
    }
}