/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * OpenNMS(R) 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 OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.protocols.snmp; import java.util.ConcurrentModificationException; import java.util.LinkedList; import java.util.ListIterator; import java.util.NoSuchElementException; /** * Provides a simple timer scheduler for use by the internal SnmpSession class. * Resolution is provided at the millisecond level. * * @see SnmpSession * * @author <a href="mailto:weave@oculan.com">Brian Weaver</a> */ class SnmpTimer extends Object { /** * The list of runnable objects (stored as TimeoutElement) */ private LinkedList<TimeoutElement> m_list; /** * The thread doing the scheduling */ private Thread m_thread; /** * when true the internal thread should exit */ private boolean m_exit; /** * The synchronization object */ private Object m_sync; /** * Used to track the individual runnables and when the runnable "expires". * */ private class TimeoutElement { /** * The runnable object */ public Runnable m_toRun; /** * The date to run the runnable */ public long m_when; /** * Default Constructor. Takes an Offset from now and a runnable that * will be executed. * * @param offset * The offset from the current time in milliseconds. * @param what * The runnable to be executed */ TimeoutElement(long offset, Runnable what) { m_when = System.currentTimeMillis() + offset; m_toRun = what; } } /** * This object is the thread of execution that monitors and executes the * scheduled runnables. * */ private class Scheduler implements Runnable { /** * Runs in an infinite loop waiting for new runnables to expire or for * the m_exit variable to be set true. The m_sync in the parent class is * used to synchronize this method * */ public void run() { LinkedList<Runnable> toRun = new LinkedList<Runnable>(); while (true) { // // synchronize on the object // synchronized (m_sync) { if (m_exit) return; // // if there are no elements on the list // then wait // if (m_list.size() == 0) { try { m_sync.wait(); } catch (InterruptedException err) { return; } // // restart the loop // continue; } // // find the smallest time slice // and run those in error // long now = System.currentTimeMillis(); boolean done = false; long minTime = Long.MAX_VALUE; ListIterator<TimeoutElement> iter = m_list.listIterator(0); while (!done && iter.hasNext()) { try { // // get the next timeout element // TimeoutElement elem = iter.next(); if (now > elem.m_when) { // // The element has expired // toRun.add(elem.m_toRun); iter.remove(); } else { // // find out if this time is less // than the one currently stored // if (elem.m_when < minTime) minTime = elem.m_when; } } catch (NoSuchElementException err) { done = true; } catch (ConcurrentModificationException err) { done = true; } } // // if there are no elements to run // then wait the minimum time until // the synchronization object is signaled. // if (toRun.size() == 0) { minTime -= now; try { if (minTime > 0) m_sync.wait(minTime); } catch (InterruptedException e) { return; } } } // end synchronization // // process the timeouts, if any // if (toRun.size() != 0) { ListIterator<Runnable> iter = toRun.listIterator(0); try { while (true) { Runnable runner = iter.next(); iter.remove(); runner.run(); } } catch (NoSuchElementException err) { // do nothing } catch (Throwable err) { // // Bad, Bad Runnable! // } } } // end while loop }// end run method } // end inner class /** * Creates an SnmpTime object and it's internal thread that is used to * schedule the execution of the runnables. * */ SnmpTimer() { m_exit = false; m_sync = new Object(); m_list = new LinkedList<TimeoutElement>(); m_thread = new Thread(new Scheduler(), "SnmpTimer"); m_thread.start(); } /** * Schedules the runnable to be run after AT LEAST ms milliseconds of time * has expired. The runnable may be invoked in a delayed manner, but will * not be run BEFORE ms milliseconds have expired. * * @param runner * The runnable object * @param milliseconds * The number of milliseconds to wait * */ void schedule(Runnable runner, long milliseconds) { if (runner != null) { synchronized (m_sync) { m_list.add(new TimeoutElement(milliseconds, runner)); m_sync.notify(); } } } /** * Cancels the current timer object and terminates the internal thread * */ void cancel() { synchronized (m_sync) { m_exit = true; m_sync.notify(); } try { // // Do not allow the timer thread to join // itself. This will cause a deadlock // condition to occur! // if (m_thread.equals(Thread.currentThread()) == false) m_thread.join(); } catch (InterruptedException err) { Thread.currentThread().interrupt(); } } } // end of class