Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
StateHolder |
|
| 0.0;0 | ||||
StateHolder$ConditionListener |
|
| 0.0;0 | ||||
StateHolder$EqualConditionListener |
|
| 0.0;0 | ||||
StateHolder$EventListener |
|
| 0.0;0 | ||||
StateHolder$NotEqualConditionListener |
|
| 0.0;0 |
1 | /* | |
2 | * | |
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. | |
4 | * | |
5 | * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. | |
6 | * | |
7 | * The contents of this file are subject to the terms of either the GNU | |
8 | * General Public License Version 2 only ("GPL") or the Common Development | |
9 | * and Distribution License("CDDL") (collectively, the "License"). You | |
10 | * may not use this file except in compliance with the License. You can obtain | |
11 | * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html | |
12 | * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific | |
13 | * language governing permissions and limitations under the License. | |
14 | * | |
15 | * When distributing the software, include this License Header Notice in each | |
16 | * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt. | |
17 | * Sun designates this particular file as subject to the "Classpath" exception | |
18 | * as provided by Sun in the GPL Version 2 section of the License file that | |
19 | * accompanied this code. If applicable, add the following below the License | |
20 | * Header, with the fields enclosed by brackets [] replaced by your own | |
21 | * identifying information: "Portions Copyrighted [year] | |
22 | * [name of copyright owner]" | |
23 | * | |
24 | * Contributor(s): | |
25 | * | |
26 | * If you wish your version of this file to be governed by only the CDDL or | |
27 | * only the GPL Version 2, indicate your decision by adding "[Contributor] | |
28 | * elects to include this software in this distribution under the [CDDL or GPL | |
29 | * Version 2] license." If you don't indicate a single choice of license, a | |
30 | * recipient has the option to distribute your version of this file under | |
31 | * either the CDDL, the GPL Version 2 or to extend the choice of license to | |
32 | * its licensees as provided above. However, if you add GPL Version 2 code | |
33 | * and therefore, elected the GPL Version 2 license, then the option applies | |
34 | * only if the new code is made subject to such option by the copyright | |
35 | * holder. | |
36 | * | |
37 | */ | |
38 | ||
39 | package com.sun.grizzly.util; | |
40 | ||
41 | import com.sun.grizzly.Controller; | |
42 | import java.util.Iterator; | |
43 | import java.util.Map; | |
44 | import java.util.concurrent.Callable; | |
45 | import java.util.concurrent.ConcurrentHashMap; | |
46 | import java.util.concurrent.CountDownLatch; | |
47 | import java.util.concurrent.atomic.AtomicReference; | |
48 | import java.util.concurrent.locks.ReentrantReadWriteLock; | |
49 | import java.util.logging.Level; | |
50 | ||
51 | /** | |
52 | * Class, which holds the state. | |
53 | * Provides API for state change notification, state read/write access locking. | |
54 | * | |
55 | * @author Alexey Stashok | |
56 | */ | |
57 | public class StateHolder<E> { | |
58 | private AtomicReference<E> stateRef; | |
59 | ||
60 | private ReentrantReadWriteLock readWriteLock; | |
61 | private volatile boolean isLockEnabled; | |
62 | ||
63 | private Map<ConditionListener<E>, Object> conditionListeners; | |
64 | ||
65 | /** | |
66 | * Constructs {@link StateHolder}. | |
67 | * StateHolder will work in not-locking mode. | |
68 | */ | |
69 | public StateHolder() { | |
70 | 0 | this(false); |
71 | 0 | } |
72 | ||
73 | /** | |
74 | * Constructs {@link StateHolder}. | |
75 | * @param isLockEnabled locking mode | |
76 | */ | |
77 | 229 | public StateHolder(boolean isLockEnabled) { |
78 | 229 | stateRef = new AtomicReference<E>(); |
79 | 229 | readWriteLock = new ReentrantReadWriteLock(); |
80 | 229 | conditionListeners = new ConcurrentHashMap<ConditionListener<E>, Object>(); |
81 | 229 | this.isLockEnabled = isLockEnabled; |
82 | 229 | } |
83 | ||
84 | /** | |
85 | * Gets current state | |
86 | * Current StateHolder locking mode will be used | |
87 | * @return state | |
88 | */ | |
89 | public E getState() { | |
90 | 252 | return getState(isLockEnabled); |
91 | } | |
92 | ||
93 | /** | |
94 | * Gets current state | |
95 | * @param locked if true, get will be invoked in locking mode, false - non-locked | |
96 | * @return state | |
97 | */ | |
98 | public E getState(boolean locked) { | |
99 | 1499445 | if (locked) { |
100 | 252 | readWriteLock.readLock().lock(); |
101 | } | |
102 | ||
103 | 1499445 | E retState = stateRef.get(); |
104 | ||
105 | 1499445 | if (locked) { |
106 | 252 | readWriteLock.readLock().unlock(); |
107 | } | |
108 | 1499445 | return retState; |
109 | } | |
110 | ||
111 | /** | |
112 | * Sets current state | |
113 | * Current StateHolder locking mode will be used | |
114 | * @param state | |
115 | */ | |
116 | public void setState(E state) { | |
117 | 1284 | setState(state, isLockEnabled); |
118 | 1284 | } |
119 | ||
120 | /** | |
121 | * Sets current state | |
122 | * @param state | |
123 | * @param locked if true, set will be invoked in locking mode, false - non-locking | |
124 | */ | |
125 | public void setState(E state, boolean locked) { | |
126 | 1422 | if (locked) { |
127 | 1284 | readWriteLock.writeLock().lock(); |
128 | } | |
129 | ||
130 | 1422 | stateRef.set(state); |
131 | ||
132 | // downgrading lock to read | |
133 | 1422 | if (locked) { |
134 | 1284 | readWriteLock.readLock().lock(); |
135 | 1284 | readWriteLock.writeLock().unlock(); |
136 | } | |
137 | ||
138 | 1422 | checkConditionListeners(state); |
139 | ||
140 | 1422 | if (locked) { |
141 | 1284 | readWriteLock.readLock().unlock(); |
142 | } | |
143 | 1422 | } |
144 | ||
145 | /** | |
146 | * Gets Read/Write locker, which is used by this {@link StateHolder} | |
147 | * @return locker | |
148 | */ | |
149 | public ReentrantReadWriteLock getStateLocker() { | |
150 | 394 | return readWriteLock; |
151 | } | |
152 | ||
153 | /** | |
154 | * Gets current locking mode | |
155 | * @return true, if mode is set to locking, false otherwise | |
156 | */ | |
157 | public boolean isLockEnabled() { | |
158 | 0 | return isLockEnabled; |
159 | } | |
160 | ||
161 | /** | |
162 | * Setss current locking mode | |
163 | * @param isLockEnabled true, if mode will be set to locking, false otherwise | |
164 | */ | |
165 | public void setLockEnabled(boolean isLockEnabled) { | |
166 | 0 | this.isLockEnabled = isLockEnabled; |
167 | 0 | } |
168 | ||
169 | /** | |
170 | * Register listener, which will be notified, when state will be equal to passed | |
171 | * one. Once listener will be notified - it will be removed from this | |
172 | * {@link StateHolder}'s listener set. | |
173 | * @param state State, listener is interested in | |
174 | * @param listener Object, which will be notified. This {@link StateHolder} | |
175 | * implementation works with Runnable, Callable, CountDownLatch, Object | |
176 | * listeners | |
177 | * @return <code>ConditionListener</code>, if current state is not equal to required | |
178 | * and listener was registered, null if current state is equal to required. | |
179 | * In both cases listener will be notified | |
180 | */ | |
181 | public ConditionListener<E> notifyWhenStateIsEqual(E state, Object listener) { | |
182 | 6 | boolean isLockEnabledLocal = isLockEnabled; |
183 | 6 | if (isLockEnabledLocal) { |
184 | 6 | getStateLocker().writeLock().lock(); |
185 | } | |
186 | ||
187 | 6 | ConditionListener<E> conditionListener = null; |
188 | ||
189 | 6 | if (stateRef.get().equals(state)) { |
190 | 1 | EventListener.notifyListener(listener); |
191 | } else { | |
192 | 5 | conditionListener = new EqualConditionListener<E>(); |
193 | 5 | EventListener eventListener = new EventListener(); |
194 | 5 | eventListener.set(listener); |
195 | 5 | conditionListener.set(state, eventListener); |
196 | ||
197 | 5 | conditionListeners.put(conditionListener, this); |
198 | } | |
199 | ||
200 | 6 | if (isLockEnabledLocal) { |
201 | 6 | getStateLocker().writeLock().unlock(); |
202 | } | |
203 | ||
204 | 6 | return conditionListener; |
205 | } | |
206 | ||
207 | /** | |
208 | * Register listener, which will be notified, when state will become not equal | |
209 | * to passed one. Once listener will be notified - it will be removed from | |
210 | * this {@link StateHolder}'s listener set. | |
211 | * @param state State, listener is interested in | |
212 | * @param listener Object, which will be notified. This {@link StateHolder} | |
213 | * implementation works with Runnable, Callable, CountDownLatch, Object | |
214 | * listeners | |
215 | * @return <code>ConditionListener</code>, if current state is equal to required | |
216 | * and listener was registered, null if current state is not equal to required. | |
217 | * In both cases listener will be notified | |
218 | */ | |
219 | public ConditionListener<E> notifyWhenStateIsNotEqual(E state, Object listener) { | |
220 | 4 | boolean isLockEnabledLocal = isLockEnabled; |
221 | 4 | if (isLockEnabledLocal) { |
222 | 4 | getStateLocker().writeLock().lock(); |
223 | } | |
224 | ||
225 | 4 | ConditionListener<E> conditionListener = null; |
226 | ||
227 | 4 | if (!stateRef.get().equals(state)) { |
228 | 1 | EventListener.notifyListener(listener); |
229 | } else { | |
230 | 3 | conditionListener = new NotEqualConditionListener<E>(); |
231 | 3 | EventListener eventListener = new EventListener(); |
232 | 3 | eventListener.set(listener); |
233 | 3 | conditionListener.set(state, eventListener); |
234 | ||
235 | 3 | conditionListeners.put(conditionListener, this); |
236 | } | |
237 | ||
238 | 4 | if (isLockEnabledLocal) { |
239 | 4 | getStateLocker().writeLock().unlock(); |
240 | } | |
241 | ||
242 | 4 | return conditionListener; |
243 | } | |
244 | ||
245 | /** | |
246 | * Register custom condition listener, which will be notified, when listener's | |
247 | * condition will become true. Once listener will be notified - it will be | |
248 | * removed from this {@link StateHolder}'s listener set. | |
249 | * @param conditionListener contains both condition and listener, which will be | |
250 | * called, when condition become true | |
251 | */ | |
252 | public void notifyWhenConditionMatchState(ConditionListener<E> conditionListener) { | |
253 | 0 | boolean isLockEnabledLocal = isLockEnabled; |
254 | 0 | if (isLockEnabledLocal) { |
255 | 0 | getStateLocker().writeLock().lock(); |
256 | } | |
257 | ||
258 | 0 | E currentState = getState(); |
259 | ||
260 | 0 | if (conditionListener.check(currentState)) { |
261 | 0 | conditionListener.notifyListener(); |
262 | } else { | |
263 | 0 | conditionListeners.put(conditionListener, this); |
264 | } | |
265 | ||
266 | 0 | if (isLockEnabledLocal) { |
267 | 0 | getStateLocker().writeLock().unlock(); |
268 | } | |
269 | 0 | } |
270 | ||
271 | public void removeConditionListener(ConditionListener<E> conditionListener) { | |
272 | 6 | if (conditionListener == null) return; |
273 | ||
274 | 6 | conditionListeners.remove(conditionListener); |
275 | 6 | } |
276 | ||
277 | protected void checkConditionListeners(E state) { | |
278 | 1422 | Iterator<ConditionListener<E>> it = conditionListeners.keySet().iterator(); |
279 | 1426 | while(it.hasNext()) { |
280 | 4 | ConditionListener<E> listener = it.next(); |
281 | try { | |
282 | 4 | if (listener.check(state)) { |
283 | 3 | it.remove(); |
284 | 3 | listener.notifyListener(); |
285 | } | |
286 | 0 | } catch(Exception e) { |
287 | 0 | Controller.logger().log(Level.WARNING, "Error calling ConditionListener", e); |
288 | 4 | } |
289 | 4 | } |
290 | 1422 | } |
291 | ||
292 | /** | |
293 | * Common ConditionListener class, which could be used with StateHolder, to | |
294 | * register custom conditions. | |
295 | * | |
296 | * On each state change - condition will be checked, if it's true - Condition's | |
297 | * listener will be notified. | |
298 | */ | |
299 | 8 | public static abstract class ConditionListener<E> { |
300 | public E state; | |
301 | public EventListener listener; | |
302 | ||
303 | protected void set(E state, EventListener listener) { | |
304 | 8 | this.state = state; |
305 | 8 | this.listener = listener; |
306 | 8 | } |
307 | ||
308 | public void notifyListener() { | |
309 | 3 | listener.notifyEvent(); |
310 | 3 | } |
311 | ||
312 | public abstract boolean check(E state); | |
313 | } | |
314 | ||
315 | /** | |
316 | * Equal ConditionListener implementation | |
317 | * @param E state class | |
318 | */ | |
319 | 5 | public static class EqualConditionListener<E> extends ConditionListener<E> { |
320 | public boolean check(E state) { | |
321 | 3 | return state.equals(this.state); |
322 | } | |
323 | } | |
324 | ||
325 | /** | |
326 | * Not equal ConditionListener implementation | |
327 | * @param E state class | |
328 | */ | |
329 | 3 | public static class NotEqualConditionListener<E> extends ConditionListener<E> { |
330 | public boolean check(E state) { | |
331 | 1 | return !state.equals(this.state); |
332 | } | |
333 | } | |
334 | ||
335 | /** | |
336 | * EventListener class, which is a part of | |
337 | * <codE>EventConditionListener</code>, and implements notificatation logic, | |
338 | * when condition becomes true. | |
339 | */ | |
340 | 8 | public static class EventListener { |
341 | public Object notificationObject; | |
342 | ||
343 | public void set(Object notificationObject) { | |
344 | 8 | this.notificationObject = notificationObject; |
345 | 8 | } |
346 | ||
347 | public void notifyEvent() { | |
348 | 3 | notifyListener(notificationObject); |
349 | 3 | } |
350 | ||
351 | protected static void notifyListener(Object listener) { | |
352 | 5 | if (listener instanceof CountDownLatch) { |
353 | 5 | ((CountDownLatch) listener).countDown(); |
354 | 0 | } else if (listener instanceof Callable) { |
355 | try { | |
356 | 0 | ((Callable) listener).call(); |
357 | 0 | } catch(Exception e) { |
358 | 0 | throw new RuntimeException(e); |
359 | 0 | } |
360 | 0 | } else if (listener instanceof Runnable) { |
361 | 0 | ((Runnable) listener).run(); |
362 | } else { | |
363 | 0 | synchronized(listener) { |
364 | 0 | listener.notify(); |
365 | 0 | } |
366 | } | |
367 | 5 | } |
368 | } | |
369 | } |