View Javadoc

1   /*
2    * Copyright (C) 2006  Tom Gibara
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not, write to the Free Software
16   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17   */
18  package com.tomgibara.pronto.util;
19  
20  /**
21   * An instance of this object can wait on an object's monitor until a condition
22   * is met, or a given number of milliseconds has elapsed - which ever occurs
23   * soonest. The class expects that it will be notified via the specified monitor
24   * when the condition may have changed.
25   * 
26   * This class is safe for multithreaded use.
27   * 
28   * @author Tom Gibara
29   */
30  
31  //TODO consider how this class may be tested
32  public class Waiter {
33  
34      // fields
35  
36      /**
37       * The object on which to wait for a possible change in condition.
38       */
39  
40      private final Object monitor;
41  
42      // constructors
43  
44      /**
45       * Creates a new waiter for waiting on the supplied object.
46       * 
47       * @param monitor
48       *            the object on which the waiter waits for a change in
49       *            condition.
50       */
51  
52      public Waiter(final Object monitor) {
53          Arguments.notNull(monitor, "monitor");
54          this.monitor = monitor;
55      }
56  
57      // accessors
58  
59      /**
60       * The monitor on which this object will wait for changes in condition.
61       * 
62       * @return the monitor on which this object will wait while waiting for a
63       *         condition to be met.
64       */
65  
66      public Object getMonitor() {
67          return monitor;
68      }
69  
70      // public methods
71  
72      /**
73       * This method will cause the calling thread to wait until the specified
74       * condtion is met or a timeout occurs, which ever is soonest. This class
75       * expects that the monitor will be notified when a condition is met. By
76       * default, this method will not abruptly in response to an interrupted
77       * exception, but this behavior may be modified by overriding the
78       * handleInterruption method.
79       * 
80       * @param timeout
81       *            the number of milliseconds to wait for, or 0 to wait
82       *            indefinitely
83       * @param cond
84       *            the condition for a timely return from this method
85       * 
86       * @return true if the condition was met, false otherwise
87       */
88  
89      public boolean waitForCondition(final long timeout, final Condition cond) {
90          Arguments.notNegative(timeout, "timeout");
91          Arguments.notNull(cond, "cond");
92  
93          if (timeout == 0) {
94              while (!cond.isMet()) {
95                  synchronized (monitor) {
96                      try {
97                          monitor.wait();
98                      } catch (InterruptedException e) {
99                          if (!handleInterruption(e)) return cond.isMet();
100                     }
101                 }
102             }
103             return true;
104         } else {
105             final long start = System.currentTimeMillis();
106             while (!cond.isMet()) {
107                 final long time = start + timeout - System.currentTimeMillis();
108                 if (time <= 0) return cond.isMet();
109                 synchronized (monitor) {
110                     try {
111                         monitor.wait(time);
112                     } catch (InterruptedException e) {
113                         if (!handleInterruption(e)) return cond.isMet();
114                     }
115                 }
116             }
117             return true;
118         }
119     }
120 
121     // methods for overriding
122 
123     /**
124      * This method is called when an interruption occurs while waiting. The
125      * default implementation of this method returns true without performing any
126      * other action. The method may be overridden to change the response of this
127      * class to any interruptions that occur during waits.
128      * 
129      * @param e
130      *            the interrupting exception
131      * 
132      * @return whether the object should continue to wait for the condition to
133      *         be met
134      */
135 
136     protected boolean handleInterruption(final InterruptedException e) {
137         return true;
138     }
139 
140     // inner classes
141 
142     /**
143      * Implementations of this interface are supplied to a Waiter and control
144      * when the object may cease waiting.
145      */
146 
147     public interface Condition {
148 
149         /**
150          * @return true if the condition has been met to stop waiting, false
151          *         otherwise
152          */
153 
154         boolean isMet();
155 
156     }
157 
158 }