Mega Code Archive

 
Categories / Java / Threads
 

Timer class used to implement login and query timeouts

// jTDS JDBC Driver for Microsoft SQL Server and Sybase // Copyright (C) 2004 The jTDS Project // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA // import java.util.LinkedList; import java.util.ListIterator; /**  * Simple timer class used to implement login and query timeouts.  * <p/>  * This thread runs as a Daemon thread to ensure that the java VM will exit  * correctly when normal execution is complete.  * <p/>  * It provides both a singleton implementation and a default constructor for  * the case when more than one timer thread is desired.  *  * @author Alin Sinpalean  * @author Mike Hutchinson  * @version $Id: TimerThread.java,v 1.5 2005/04/28 14:29:31 alin_sinpalean Exp $  */ public class TimerThread extends Thread {     /**      * Interface to be implemented by classes that request timer services.      */     public interface TimerListener {         /**          * Event to be fired when the timeout expires.          */         void timerExpired();     }     /**      * Internal class associating a login or query timeout value with a target      * <code>TimerListener</code>.      */     private static class TimerRequest {         /** The time when this timeout will expire. */         final long time;         /** Target to notify when the timeout expires. */         final TimerListener target;         /**          * Create a <code>TimerRequest</code>.          *          * @param timeout the desired timeout in milliseconds          * @param target  the target object; one of <code>SharedSocket</code> or          *                <code>TdsCore</code>          * @throws IllegalArgumentException if the timeout is negative or 0          */         TimerRequest(int timeout, TimerListener target) {             if (timeout <= 0) {                 throw new IllegalArgumentException("Invalid timeout parameter "                         + timeout);             }             this.time = System.currentTimeMillis() + (timeout);             this.target = target;         }     }     /** Singleton instance. */     private static TimerThread instance;     /** List of <code>TimerRequest</code>s to execute, ordered by time. */     private final LinkedList timerList = new LinkedList();     /** Time when the first request time out should occur. */     private long nextTimeout;     /**      * Singleton getter.      */     public static synchronized TimerThread getInstance() {         if (instance == null) {             instance = new TimerThread();             instance.start();         }         return instance;     }     /**      * Construct a new <code>TimerThread</code> instance.      */     public TimerThread() {         // Set the thread name         super("jTDS TimerThread");         // Ensure that this thread does not prevent the VM from exiting         this.setDaemon(true);     }     /**      * Execute the <code>TimerThread</code> main loop.      */     public void run() {         synchronized (timerList) {             while (true) {                 try {                     try {                         // If nextTimeout == 0 (i.e. there are no more requests                         // in the queue) wait indefinitely -- wait(0)                         timerList.wait(nextTimeout == 0 ? 0                                 : nextTimeout - System.currentTimeMillis());                     } catch (IllegalArgumentException ex) {                         // Timeout was negative, fire timeout                     }                     // Fire expired timeout requests                     long time = System.currentTimeMillis();                     while (!timerList.isEmpty()) {                         // Examime the head of the list and see                         // if the timer has expired.                         TimerRequest t = (TimerRequest) timerList.getFirst();                         if (t.time > time) {                             break; // No timers have expired                         }                         // Notify target of timeout                         t.target.timerExpired();                         // Remove the fired timeout request                         timerList.removeFirst();                     }                     // Determine next timeout                     updateNextTimeout();                 } catch (InterruptedException e) {                     // nop                 }             }         }     }     /**      * Add a timer request to the queue.      * <p/>      * The queue is ordered by time so that the head of the list is always the      * first timer to expire.      *      * @param timeout the interval in milliseconds after which the timer will      *                expire      * @param l       <code>TimerListener</code> to be notified on timeout      * @return a handle to the timer request, that can later be used with      *         <code>cancelTimer</code>      */     public Object setTimer(int timeout, TimerListener l) {         // Create a new timer request         TimerRequest t = new TimerRequest(timeout, l);         synchronized (timerList) {             if (timerList.isEmpty()) {                 // List was empty, just add new request                 timerList.add(t);             } else {                 // Tiny optimization; new requests will usually go to the end                 TimerRequest crt = (TimerRequest) timerList.getLast();                 if (t.time >= crt.time) {                     timerList.addLast(t);                 } else {                     // Iterate the list and insert it into the right place                     for (ListIterator li = timerList.listIterator(); li.hasNext(); ) {                         crt = (TimerRequest) li.next();                         if (t.time < crt.time) {                             li.previous();                             li.add(t);                             break;                         }                     }                 }             }             // If this request is now the first in the list, interupt timer             if (timerList.getFirst() == t) {                 nextTimeout = t.time;                 this.interrupt();             }         }         // Return the created request as timer handle         return t;     }     /**      * Remove a redundant timer before it expires.      *      * @param handle handle to the request to be removed from the queue (a      *        <code>TimerRequest</code> instance)      * @return <code>true</code> if timer had not expired      */     public boolean cancelTimer(Object handle) {         TimerRequest t = (TimerRequest) handle;         synchronized (timerList) {             boolean result = timerList.remove(t);             if (nextTimeout == t.time) {                 updateNextTimeout();             }             return result;         }     }     /**      * Check whether a timer has expired.      *      * @param handle handle to the request to be checked for expiry (a      *        <code>TimerRequest</code> instance)      * @return <code>true</code> if timer has expired      */     public boolean hasExpired(Object handle) {         TimerRequest t = (TimerRequest) handle;         synchronized (timerList) {             return !timerList.contains(t);         }     }     /** Internal method that updates the value of {@link #nextTimeout}. */     private void updateNextTimeout() {         nextTimeout = timerList.isEmpty() ? 0                 : ((TimerRequest) timerList.getFirst()).time;     } }