##### Coverage Report - com.sun.grizzly.util.StateHolder

Classes in this File | Line Coverage | Branch Coverage | Complexity
StateHolder |
80 % | 66/83
68 % | 23/34
| 0.0;0
StateHolder$ConditionListener |
100 % | 6/6
N/A | N/A
| 0.0;0
StateHolder$EqualConditionListener |
100 % | 2/2
N/A | N/A
| 0.0;0
StateHolder$EventListener |
44 % | 8/18
17 % | 1/6
| 0.0;0
StateHolder$NotEqualConditionListener |
100 % | 2/2
50 % | 1/2
| 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 | 261 |
             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 | 1598385 |
             if (locked) {
100 | 261 |
                 readWriteLock.readLock().lock();
101 |  |
             }
102 |  |
103 | 1598385 |
             E retState = stateRef.get();
104 |  |
105 | 1598385 |
             if (locked) {
106 | 261 |
                 readWriteLock.readLock().unlock();
107 |  |
             }
108 | 1598385 |
             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 |  |
     }