View Javadoc

1   package cz.cuni.amis.utils.flag;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.List;
6   import java.util.concurrent.CountDownLatch;
7   import java.util.concurrent.TimeUnit;
8   
9   import cz.cuni.amis.utils.collections.MyCollections;
10  import cz.cuni.amis.utils.exception.PogamutInterruptedException;
11  
12  /**
13   * This class is implementing the waiting on some flag value.
14   * <BR><BR>
15   * Note that you may call only one AWAIT() at time.
16   * <BR><BR>
17   * Typical usage:
18   * <BR>
19   * boolean flagValue = new WaitForFlagChange&lt;Boolean&gt;(booleanFlag, Boolean.TRUE).await();
20   * <BR>
21   * int flagValue = new WaitForFlagChange&lt;Integer&gt;(integerFlag, new Integer[]{1,3}).await();
22   * 
23   * @author Jimmy
24   *
25   * @param <TYPE>
26   */
27  public class WaitForFlagChange<TYPE> {
28  	
29  	public static interface IAccept<TYPE> {
30  		
31  		public boolean accept(TYPE flagValue);
32  		
33  	}
34  	
35  	private class ListAccept implements IAccept<TYPE> {
36  		
37  		private List<TYPE> waitingFor;
38  		
39  		public ListAccept(Collection<TYPE> list) {
40  			waitingFor = new ArrayList<TYPE>(list.size());
41  			waitingFor.addAll(list);
42  		}
43  		
44  		/**
45  		 * Accepts all.
46  		 */
47  		public ListAccept() {
48  			this.waitingFor = null;
49  		}
50  		
51  		public ListAccept(TYPE... accept) {
52  			waitingFor = new ArrayList(accept.length);
53  			waitingFor.addAll(MyCollections.asList(accept));
54  		}
55  
56  		@Override
57  		public boolean accept(TYPE flagValue) {
58  			if (waitingFor == null) return true;
59  			return waitingFor.contains(flagValue);
60  		}
61  		
62  	}
63  	
64  	private IAccept<TYPE> accept;
65  	private IFlag<TYPE> flag;
66  	
67  	private Object latchAccessMutex = new Object();
68  	private Object mutex = new Object();
69  	
70  	private CountDownLatch latch = null;
71  	
72  	private boolean isResult = false;
73  	private TYPE result = null;
74  	
75  	private class Listener implements FlagListener<TYPE> {
76  		
77  		public Listener(IFlag<TYPE> flag) {
78  			WaitForFlagChange.this.result = flag.getFlag();			
79  			if (!(isResult = isDesiredValue(WaitForFlagChange.this.result))) {
80  				flag.addListener(this);
81  			}
82  			// must do second check because of the flag that has been just attached
83  			if (!isResult) {
84  				TYPE result = flag.getFlag();
85  				if (isDesiredValue(result)) { 
86  					isResult = true;
87  					WaitForFlagChange.this.result = result;
88  					flag.removeListener(this);
89  					synchronized(latchAccessMutex) {
90  						if (latch != null) {
91  							latch.countDown();
92  						}
93  					}	
94  				}
95  			}
96  		}
97  
98  		@Override
99  		public void flagChanged(TYPE changedValue) {
100 			if (!isResult && isDesiredValue(changedValue)) {
101 				flag.removeListener(this);
102 				synchronized(latchAccessMutex) {
103 					isResult = true;
104 					result = changedValue;
105 					if (latch != null) {
106 						latch.countDown();
107 					}
108 				}				
109 			}
110 		}
111 		
112 	}
113 	
114 	/**
115 	 * Wait for the next flag change.
116 	 * @param flag
117 	 */
118 	public WaitForFlagChange(IFlag<TYPE> flag) {
119 		this.flag = flag;
120 		this.accept = new ListAccept();
121 	}
122 	
123 	public WaitForFlagChange(IFlag<TYPE> flag, IAccept<TYPE> waitingFor) {
124 		this.flag = flag;
125 		this.accept = waitingFor;
126 	}
127 	
128 	public WaitForFlagChange(IFlag<TYPE> flag, TYPE waitingFor) {
129 		this.flag = flag;
130 		this.accept = new ListAccept(waitingFor);
131 	}
132 	
133 	public WaitForFlagChange(IFlag<TYPE> flag, TYPE[] waitingFor) {
134 		this.flag = flag;
135 		this.accept = new ListAccept(waitingFor);		
136 	}
137 	
138 	public WaitForFlagChange(IFlag<TYPE> flag, Collection<TYPE> waitingFor) {
139 		this.flag = flag;
140 		this.accept = new ListAccept(waitingFor);		
141 	}
142 		
143 	private boolean isDesiredValue(TYPE value) {
144 		return accept.accept(value);
145 	}
146 	
147 	/**
148 	 * Note that you may call only await() from one thread! If the instance is already in used
149 	 * it may produce unwanted behavior (e.g. dead-lock).
150 	 * 
151 	 * @return value from the flag that raised the latch
152 	 * @throws InterruptedException
153 	 */
154 	public TYPE await() throws PogamutInterruptedException {
155 		synchronized(mutex) {
156 			synchronized(latchAccessMutex) {
157 				latch = new CountDownLatch(1);
158 			}
159 			// instantiation checks whether we doesn't have desired result already, 
160 			// if not adds itself as a listener to a flag
161 			Listener listener = new Listener(flag); 
162 			if (isResult) return result;			
163 			try {
164 				latch.await(); // the latch is raised whenever a listener receive a correct result
165 			} catch (InterruptedException e) {
166 				throw new PogamutInterruptedException(e, this);
167 			}
168 			synchronized(latchAccessMutex) {
169 				flag.removeListener(listener);
170 				latch = null;
171 			}
172 			return result;
173 		}
174 	}
175 	
176 	/**
177 	 * Note that you may call only await() from one thread! If the instance is already in used
178 	 * it may produce unwanted behavior.
179 	 * <p><p>
180 	 * Returns null if desired value hasn't been set at the flag before timeout.
181 	 * 
182 	 * @param timeout
183 	 * @param timeUnit
184 	 * @return value of the flag
185 	 * @throws PogamutInterruptedException
186 	 */
187 	public TYPE await(long timeout, TimeUnit timeUnit) throws PogamutInterruptedException {
188 		synchronized(mutex) {
189 			synchronized(latchAccessMutex) {
190 				latch = new CountDownLatch(1);
191 			}
192 			// instantiation checks whether we doesn't have desired result already, 
193 			// if not adds itself as a listener to a flag
194 			Listener listener = new Listener(flag); 
195 			if (isResult) return result;			
196 			try {
197 				latch.await(timeout, timeUnit); // the latch is raised whenever a listener receive a correct result
198 			} catch (InterruptedException e) {
199 				throw new PogamutInterruptedException(e, this);
200 			}
201 			synchronized(latchAccessMutex) {
202 				flag.removeListener(listener);
203 				latch = null;
204 			}
205 			return result;
206 		}
207 	}
208 
209 }