Coverage Report - com.sun.grizzly.util.StateHolder
 
Classes in this File Line Coverage Branch Coverage Complexity
StateHolder
80 %
66/83
68 %
23/34
0
StateHolder$ConditionListener
100 %
6/6
N/A
0
StateHolder$EqualConditionListener
100 %
2/2
N/A
0
StateHolder$EventListener
44 %
8/18
17 %
1/6
0
StateHolder$NotEqualConditionListener
100 %
2/2
50 %
1/2
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  
 }