# This patch file was generated by NetBeans IDE # Following Index: paths are relative to: C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly # This patch can be applied using context Tools: Patch action on respective folder. # It uses platform neutral UTF-8 encoding and \n newlines. # Above lines and this line are ignored by the patching process. Index: Controller.java --- Controller.java Base (BASE) +++ Controller.java Locally Modified (Based On LOCAL) @@ -26,6 +26,9 @@ import com.sun.grizzly.util.AttributeHolder; import com.sun.grizzly.util.Cloner; import com.sun.grizzly.util.Copyable; +import com.sun.grizzly.util.State; +import com.sun.grizzly.util.StateHolder; +import com.sun.grizzly.util.SupportStateHolder; import java.io.IOException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedSelectorException; @@ -40,7 +43,6 @@ import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; @@ -125,7 +127,7 @@ * @author Jeanfrancois Arcand */ public class Controller implements Runnable, Lifecycle, Copyable, - ConnectorHandlerPool, AttributeHolder { + ConnectorHandlerPool, AttributeHolder, SupportStateHolder { public enum Protocol { UDP, TCP , TLS, CUSTOM } @@ -173,27 +175,12 @@ /** - * Controller state enum - * STOPPED - Controller is in a stopped, not running state - * STARTED - Controller is in a started, running state - * + * Current Controller state */ - protected enum State { STOPPED, STARTED } + protected StateHolder stateHolder; /** - * Current Controller state - */ - protected volatile State state; - - - /** - * State lock to have consistent state value - */ - protected ReentrantLock stateLock; - - - /** * The number of read threads */ protected int readThreadsCount = 0; @@ -253,7 +240,7 @@ */ public Controller() { contexts = new ConcurrentLinkedQueue(); - stateLock = new ReentrantLock(); + stateHolder = new StateHolder(true); } @@ -301,7 +288,9 @@ readyKeys = selectorHandler.select(serverCtx); selectorState = readyKeys.size(); - if (state == State.STARTED && selectorState != 0) { + if (stateHolder.getState(false) == State.STARTED && + selectorHandler.getStateHolder().getState(false) == State.STARTED && + selectorState != 0) { iterator = readyKeys.iterator(); while (iterator.hasNext()) { key = iterator.next(); @@ -398,20 +387,17 @@ // shutting down. Hence, we need to handle this Exception // appropriately. Perhaps check the state before logging // what's happening ? - stateLock.lock(); - try { - if (state != State.STOPPED) { + if (stateHolder.getState() == State.STARTED && + selectorHandler.getStateHolder().getState() == State.STARTED) { logger.log(Level.SEVERE, "Selector was unexpectedly closed."); notifyException(e); } else { logger.log(Level.FINE, "doSelect Selector closed"); } - } finally { - stateLock.unlock(); - } } catch (ClosedChannelException e) { // Don't use stateLock. This case is not strict - if (state != State.STOPPED) { + if (stateHolder.getState() == State.STARTED && + selectorHandler.getStateHolder().getState() == State.STARTED) { logger.log(Level.WARNING, "Channel was unexpectedly closed"); if (key != null){ selectorHandler.getSelectionKeyHandler().cancel(key); @@ -457,7 +443,7 @@ * @param protocol specified protocol SelectorHandler key should be registered on */ public void registerKey(SelectionKey key, int ops, Protocol protocol){ - if (state == State.STOPPED) { + if (stateHolder.getState() == State.STOPPED) { return; } @@ -471,7 +457,7 @@ * @deprecated */ public void cancelKey(SelectionKey key){ - if (state == State.STOPPED) { + if (stateHolder.getState() == State.STOPPED) { return; } @@ -587,7 +573,17 @@ selectorHandlers = new ConcurrentLinkedQueue(); } selectorHandlers.add(selectorHandler); + if (stateHolder.getState(false) != null && + !State.STOPPED.equals(stateHolder.getState())) { + addSelectorHandlerOnReadControllers(selectorHandler); + if (readySelectorHandlerCounter != null && + stoppedSelectorHandlerCounter != null) { + readySelectorHandlerCounter.incrementAndGet(); + stoppedSelectorHandlerCounter.incrementAndGet(); } + startSelectorHandlerRunner(selectorHandler, true); + } + } /** @@ -639,6 +635,25 @@ /** + * Shuts down SelectorHandler and removes it from this + * Controller list + * @param SelectorHandler to remove + */ + public void removeSelectorHandler(SelectorHandler selectorHandler) { + if (selectorHandlers.remove(selectorHandler)) { + if (stateHolder.getState(false) != null && + !State.STOPPED.equals(stateHolder.getState())) { + readySelectorHandlerCounter.decrementAndGet(); + stoppedSelectorHandlerCounter.decrementAndGet(); + } + + removeSelectorHandlerOnReadControllers(selectorHandler); + selectorHandler.shutdown(); + } + } + + + /** * Return the Pipeline (Thread Pool) used by this Controller. */ public Pipeline getPipeline() { @@ -715,8 +730,7 @@ copyController.readThreadControllers = readThreadControllers; copyController.readThreadsCount = readThreadsCount; copyController.selectionKeyHandler = selectionKeyHandler; - copyController.stateLock = stateLock; - copyController.state = state; + copyController.stateHolder = stateHolder; } // -------------------------------------------------------- Lifecycle ----// @@ -813,12 +827,7 @@ pipeline.initPipeline(); pipeline.startPipeline(); - stateLock.lock(); - try { - state = State.STARTED; - } finally { - stateLock.unlock(); - } + stateHolder.setState(State.STARTED); notifyStarted(); if (readThreadsCount > 0) { @@ -832,15 +841,8 @@ stoppedSelectorHandlerCounter = new AtomicInteger(selectorHandlerCount); for (SelectorHandler selectorHandler : selectorHandlers) { - Runnable selectorRunner = new SelectorHandlerRunner(this, selectorHandler); - if(selectorHandlerCount > 1) { - // if there are more than 1 selector handler - run it in separate thread - new Thread(selectorRunner).start(); - } else { - // else run it in current thread - selectorRunner.run(); + startSelectorHandlerRunner(selectorHandler, selectorHandlerCount > 1); } - } waitUntilSeletorHandlersStop(); selectorHandlers.clear(); @@ -853,10 +855,10 @@ * Stop the Controller by canceling all the registered keys. */ public void stop() throws IOException { - stateLock.lock(); + stateHolder.getStateLocker().writeLock().lock(); try { - if (state != State.STOPPED) { - state = State.STOPPED; + if (stateHolder.getState(false) != State.STOPPED) { + stateHolder.setState(State.STOPPED, false); // TODO: Consider moving the for Controller loop below to // the end of the start() method and using a // wait() / notify() construct to shutdown this @@ -883,29 +885,38 @@ logger.log(Level.FINE, "Controller is already in stopped state"); } } finally { - stateLock.unlock(); + stateHolder.getStateLocker().writeLock().unlock(); } } /** - * Not implemented. + * Pause this Controller and associated SelectorHandlers */ public void pause() throws IOException { - ; // Not yet implemented + stateHolder.setState(State.PAUSED); } /** - * Not implemented. + * Resume this Controller and associated SelectorHandlers */ public void resume() throws IOException { - ; // Not yet implemented + stateHolder.setState(State.STARTED); } /** + * Gets this Controller's StateHolder + * @return StateHolder + */ + public StateHolder getStateHolder() { + return stateHolder; + } + + + /** * Initialize the number of ReadThreadController. */ private void initReadThreads() throws IOException { @@ -918,40 +929,80 @@ for(int i=0; iSelectorHandler on all read controllers + * @param selectorHandler + */ + private void addSelectorHandlerOnReadControllers(SelectorHandler selectorHandler) { + if (readThreadControllers == null || readThreadsCount == 0) return; + // Attributes need to be shared among SelectorHandler and its read-copies if (selectorHandler.getAttributes() == null) { selectorHandler.setAttributes(new HashMap(2)); } + for (Controller readController : readThreadControllers) { SelectorHandler copySelectorHandler = Cloner.clone(selectorHandler); copySelectorHandler.setSelector(null); - controller.addSelectorHandler(copySelectorHandler); + readController.addSelectorHandler(copySelectorHandler); } - controller.setReadThreadsCount(0); - readThreadControllers[i] = controller; - // TODO Get a Thread from a Pool instead. - new Thread(controller).start(); } + + + /** + * Starts SelectorHandlerRunner + * @param selectorHandler + * @param isRunAsync if true - SelectorHandlerRunner will be run + * in separate Thread, if false - in current Thread + */ + private void startSelectorHandlerRunner(SelectorHandler selectorHandler, + boolean isRunAsync) { + Runnable selectorRunner = new SelectorHandlerRunner(this, selectorHandler); + if(isRunAsync) { + // if there are more than 1 selector handler - run it in separate thread + new Thread(selectorRunner).start(); + } else { + // else run it in current thread + selectorRunner.run(); } + } /** + * Register SelectorHandler on all read controllers + * @param selectorHandler + */ + private void removeSelectorHandlerOnReadControllers(SelectorHandler selectorHandler) { + if (readThreadControllers == null) return; + + for (ReadController readController : readThreadControllers) { + readController.removeSelectorHandlerClone(selectorHandler); + } + } + + + /** * Is this Controller started? * @return boolean true / false */ public boolean isStarted() { - boolean result = false; - if (stateLock != null){ - stateLock.lock(); - try { - result = (state == State.STARTED); - } finally { - stateLock.unlock(); + return stateHolder.getState() == State.STARTED; } - } - return result; - } // ----------- ConnectorHandlerPool interface implementation ----------- // Index: ReadController.java --- ReadController.java Base (BASE) +++ ReadController.java Locally Modified (Based On LOCAL) @@ -27,6 +27,7 @@ import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; +import java.util.Iterator; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; @@ -42,6 +43,23 @@ public class ReadController extends Controller { /** + * Removes SelectorHandler's clone, registered + * on this Controller + * + * @param selectorHandler + */ + public void removeSelectorHandlerClone(SelectorHandler selectorHandler) { + Iterator it = selectorHandlers.iterator(); + while(it.hasNext()) { + SelectorHandler cloneSelectorHandler = it.next(); + if (cloneSelectorHandler.getStateHolder() == selectorHandler.getStateHolder()) { + removeSelectorHandler(cloneSelectorHandler); + return; + } + } + } + + /** * Add a Channel * to be processed by ReadController's * SelectorHandler @@ -78,15 +96,6 @@ */ @Override public void start() throws IOException { - stateLock.lock(); - try { - // could be null if ReadController will be used outside main Controller - if (state == null) { - state = State.STARTED; - } - } finally { - stateLock.unlock(); - } notifyStarted(); int selectorHandlerCount = selectorHandlers.size(); Index: SelectorHandler.java --- SelectorHandler.java Base (BASE) +++ SelectorHandler.java Locally Modified (Based On LOCAL) @@ -26,6 +26,8 @@ import com.sun.grizzly.async.AsyncQueueWriter; import com.sun.grizzly.util.AttributeHolder; import com.sun.grizzly.util.Copyable; +import com.sun.grizzly.util.State; +import com.sun.grizzly.util.SupportStateHolder; import java.io.IOException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; @@ -40,7 +42,8 @@ * * @author Jeanfrancois Arcand */ -public interface SelectorHandler extends Handler, Copyable, AttributeHolder { +public interface SelectorHandler extends Handler, Copyable, + AttributeHolder, SupportStateHolder { /** * A token decribing the protocol supported by an implementation of this @@ -79,6 +82,18 @@ /** + * Pause this SelectorHandler + */ + public void pause(); + + + /** + * Resume this SelectorHandler + */ + public void resume(); + + + /** * Shutdown this instance. */ public void shutdown(); Index: SelectorHandlerRunner.java --- SelectorHandlerRunner.java Base (BASE) +++ SelectorHandlerRunner.java Locally Modified (Based On LOCAL) @@ -23,8 +23,11 @@ package com.sun.grizzly; -import java.nio.channels.SelectionKey; -import com.sun.grizzly.Controller.State; +import com.sun.grizzly.util.State; +import com.sun.grizzly.util.StateHolder; +import com.sun.grizzly.util.StateHolder.ConditionListener; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Class is responsible for processing certain (single) @@ -48,25 +51,65 @@ public void run() { boolean firstTimeSelect = true; + StateHolder controllerStateHolder = controller.stateHolder; + StateHolder selectorHandlerStateHolder = selectorHandler.getStateHolder(); + try { - while (controller.state == State.STARTED) { + selectorHandler.getStateHolder().setState(State.STARTED); + + while (controllerStateHolder.getState(false) != State.STOPPED && + selectorHandlerStateHolder.getState(false) != State.STOPPED) { + + State controllerState = controllerStateHolder.getState(false); + State selectorHandlerState = selectorHandlerStateHolder.getState(false); + + if (controllerState != State.PAUSED && + selectorHandlerState != State.PAUSED) { controller.doSelect(selectorHandler); if (firstTimeSelect) { firstTimeSelect = false; controller.notifyReady(); } - } + } else { + CountDownLatch latch = new CountDownLatch(1); + ConditionListener controllerConditionListener = + registerForNotification(controllerState, + controllerStateHolder, latch); + ConditionListener selectorHandlerConditionListener = + registerForNotification(selectorHandlerState, + selectorHandlerStateHolder, latch); - SelectionKeyHandler selectionKeyHandler = selectorHandler.getSelectionKeyHandler(); - - for (SelectionKey selectionKey : selectorHandler.keys()) { - selectionKeyHandler.close(selectionKey); + try { + latch.await(5000, TimeUnit.MILLISECONDS); + } catch(InterruptedException e) { + } finally { + controllerStateHolder.removeConditionListener(controllerConditionListener); + selectorHandlerStateHolder.removeConditionListener(selectorHandlerConditionListener); } - - selectorHandler.shutdown(); + } + } } finally { + selectorHandler.shutdown(); controller.notifyStopped(); } } + + /** + * Register CountDownLatch to be notified, when either Controller + * or SelectorHandler will change their state to correspondent values + * @param currentState initial/current StateHolder state + * @param stateHolder + * @param latch + * @return ConditionListener if listener is registered, null if + * condition is true right now + */ + private ConditionListener registerForNotification(State currentState, + StateHolder stateHolder, CountDownLatch latch) { + if (currentState == State.PAUSED) { + return stateHolder.notifyWhenStateIsNotEqual(State.PAUSED, latch); + } else { + return stateHolder.notifyWhenStateIsEqual(State.STOPPED, latch); } + } +} \ No newline at end of file Index: TCPSelectorHandler.java --- TCPSelectorHandler.java Base (BASE) +++ TCPSelectorHandler.java Locally Modified (Based On LOCAL) @@ -33,6 +33,8 @@ import com.sun.grizzly.util.Copyable; import com.sun.grizzly.util.SelectionKeyOP; import com.sun.grizzly.util.SelectionKeyOP.ConnectSelectionKeyOP; +import com.sun.grizzly.util.State; +import com.sun.grizzly.util.StateHolder; import java.io.IOException; import java.net.BindException; import java.net.InetAddress; @@ -42,6 +44,7 @@ import java.net.SocketAddress; import java.net.SocketException; import java.nio.channels.ClosedChannelException; +import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; @@ -204,7 +207,10 @@ */ protected Map attributes; + protected StateHolder stateHolder = new StateHolder(true); + public TCPSelectorHandler(){ + this(false); } @@ -232,6 +238,7 @@ copyHandler.logger = logger; copyHandler.reuseAddress = reuseAddress; copyHandler.connectorInstanceHandler = connectorInstanceHandler; + copyHandler.stateHolder = stateHolder; } @@ -463,16 +470,42 @@ selector.wakeup(); } + /** + * {@inheritDoc} + */ + public void pause() { + stateHolder.setState(State.PAUSED); + } /** + * {@inheritDoc} + */ + public void resume() { + stateHolder.setState(State.STARTED); + } + + /** + * {@inheritDoc} + */ + public StateHolder getStateHolder() { + return stateHolder; + } + + /** * Shuntdown this instance by closing its Selector and associated channels. */ public void shutdown() { - if (selector != null){ + stateHolder.setState(State.STOPPED); + + if (selector != null) { + try { for (SelectionKey selectionKey : selector.keys()) { selectionKeyHandler.close(selectionKey); } + } catch (ClosedSelectorException e) { + // If Selector is already closed - OK } + } try{ if (serverSocket != null) Index: util/State.java --- util/State.java Locally New +++ util/State.java Locally New @@ -0,0 +1,36 @@ +/* + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the license at + * https://glassfish.dev.java.net/public/CDDLv1.0.html or + * glassfish/bootstrap/legal/CDDLv1.0.txt. + * See the License for the specific language governing + * permissions and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at glassfish/bootstrap/legal/CDDLv1.0.txt. + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * you own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + */ + +package com.sun.grizzly.util; + +/** + * State enum + * STOPPED - Process is stopped, not running state + * STARTED - Process is started, running state + * PAUSED - Process is paused, not processing tasks + * + * @author Alexey Stashok + */ +public enum State { + STOPPED, STARTED, PAUSED +} Index: util/StateHolder.java --- util/StateHolder.java Locally New +++ util/StateHolder.java Locally New @@ -0,0 +1,354 @@ +/* + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the license at + * https://glassfish.dev.java.net/public/CDDLv1.0.html or + * glassfish/bootstrap/legal/CDDLv1.0.txt. + * See the License for the specific language governing + * permissions and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at glassfish/bootstrap/legal/CDDLv1.0.txt. + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * you own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + */ + +package com.sun.grizzly.util; + +import com.sun.grizzly.Controller; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.logging.Level; + +/** + * Class, which holds the state. + * Provides API for state change notification, state read/write access locking. + * + * @author Alexey Stashok + */ +public class StateHolder { + private AtomicReference stateRef; + + private ReentrantReadWriteLock readWriteLock; + private volatile boolean isLockEnabled; + + private Map, Object> conditionListeners; + + /** + * Constructs StateHolder. + * StateHolder will work in not-locking mode. + */ + public StateHolder() { + this(false); + } + + /** + * Constructs StateHolder. + * @param isLockEnabled locking mode + */ + public StateHolder(boolean isLockEnabled) { + stateRef = new AtomicReference(); + readWriteLock = new ReentrantReadWriteLock(); + conditionListeners = new ConcurrentHashMap, Object>(); + this.isLockEnabled = isLockEnabled; + } + + /** + * Gets current state + * Current StateHolder locking mode will be used + * @return state + */ + public E getState() { + return getState(isLockEnabled); + } + + /** + * Gets current state + * @param locked if true, get will be invoked in locking mode, false - non-locked + * @return state + */ + public E getState(boolean locked) { + if (locked) { + readWriteLock.readLock().lock(); + } + + E retState = stateRef.get(); + + if (locked) { + readWriteLock.readLock().unlock(); + } + return retState; + } + + /** + * Sets current state + * Current StateHolder locking mode will be used + * @param state + */ + public void setState(E state) { + setState(state, isLockEnabled); + } + + /** + * Sets current state + * @param state + * @param locked if true, set will be invoked in locking mode, false - non-locking + */ + public void setState(E state, boolean locked) { + if (locked) { + readWriteLock.writeLock().lock(); + } + + stateRef.set(state); + + // downgrading lock to read + if (locked) { + readWriteLock.readLock().lock(); + readWriteLock.writeLock().unlock(); + } + + checkConditionListeners(state); + + if (locked) { + readWriteLock.readLock().unlock(); + } + } + + /** + * Gets Read/Write locker, which is used by this StateHolder + * @return locker + */ + public ReentrantReadWriteLock getStateLocker() { + return readWriteLock; + } + + /** + * Gets current locking mode + * @return true, if mode is set to locking, false otherwise + */ + public boolean isLockEnabled() { + return isLockEnabled; + } + + /** + * Setss current locking mode + * @param isLockEnabled true, if mode will be set to locking, false otherwise + */ + public void setLockEnabled(boolean isLockEnabled) { + this.isLockEnabled = isLockEnabled; + } + + /** + * Register listener, which will be notified, when state will be equal to passed + * one. Once listener will be notified - it will be removed from this + * StateHolder's listener set. + * @param state State, listener is interested in + * @param listener Object, which will be notified. This StateHolder + * implementation works with Runnable, Callable, CountDownLatch, Object + * listeners + * @return ConditionListener, if current state is not equal to required + * and listener was registered, null if current state is equal to required. + * In both cases listener will be notified + */ + public ConditionListener notifyWhenStateIsEqual(E state, Object listener) { + boolean isLockEnabledLocal = isLockEnabled; + if (isLockEnabledLocal) { + getStateLocker().writeLock().lock(); + } + + ConditionListener conditionListener = null; + + if (stateRef.get().equals(state)) { + EventListener.notifyListener(listener); + } else { + conditionListener = new EqualConditionListener(); + EventListener eventListener = new EventListener(); + eventListener.set(listener); + conditionListener.set(state, eventListener); + + conditionListeners.put(conditionListener, this); + } + + if (isLockEnabledLocal) { + getStateLocker().writeLock().unlock(); + } + + return conditionListener; + } + + /** + * Register listener, which will be notified, when state will become not equal + * to passed one. Once listener will be notified - it will be removed from + * this StateHolder's listener set. + * @param state State, listener is interested in + * @param listener Object, which will be notified. This StateHolder + * implementation works with Runnable, Callable, CountDownLatch, Object + * listeners + * @return ConditionListener, if current state is equal to required + * and listener was registered, null if current state is not equal to required. + * In both cases listener will be notified + */ + public ConditionListener notifyWhenStateIsNotEqual(E state, Object listener) { + boolean isLockEnabledLocal = isLockEnabled; + if (isLockEnabledLocal) { + getStateLocker().writeLock().lock(); + } + + ConditionListener conditionListener = null; + + if (!stateRef.get().equals(state)) { + EventListener.notifyListener(listener); + } else { + conditionListener = new NotEqualConditionListener(); + EventListener eventListener = new EventListener(); + eventListener.set(listener); + conditionListener.set(state, eventListener); + + conditionListeners.put(conditionListener, this); + } + + if (isLockEnabledLocal) { + getStateLocker().writeLock().unlock(); + } + + return conditionListener; + } + + /** + * Register custom condition listener, which will be notified, when listener's + * condition will become true. Once listener will be notified - it will be + * removed from this StateHolder's listener set. + * @param conditionListener contains both condition and listener, which will be + * called, when condition become true + */ + public void notifyWhenConditionMatchState(ConditionListener conditionListener) { + boolean isLockEnabledLocal = isLockEnabled; + if (isLockEnabledLocal) { + getStateLocker().writeLock().lock(); + } + + E currentState = getState(); + + if (conditionListener.check(currentState)) { + conditionListener.notifyListener(); + } else { + conditionListeners.put(conditionListener, this); + } + + if (isLockEnabledLocal) { + getStateLocker().writeLock().unlock(); + } + } + + public void removeConditionListener(ConditionListener conditionListener) { + if (conditionListener == null) return; + + conditionListeners.remove(conditionListener); + } + + protected void checkConditionListeners(E state) { + Iterator> it = conditionListeners.keySet().iterator(); + while(it.hasNext()) { + ConditionListener listener = it.next(); + try { + if (listener.check(state)) { + it.remove(); + listener.notifyListener(); + } + } catch(Exception e) { + Controller.logger().log(Level.WARNING, "Error calling ConditionListener", e); + } + } + } + + /** + * Common ConditionListener class, which could be used with StateHolder, to + * register custom conditions. + * + * On each state change - condition will be checked, if it's true - Condition's + * listener will be notified. + */ + public static abstract class ConditionListener { + public E state; + public EventListener listener; + + protected void set(E state, EventListener listener) { + this.state = state; + this.listener = listener; + } + + public void notifyListener() { + listener.notifyEvent(); + } + + public abstract boolean check(E state); + } + + /** + * Equal ConditionListener implementation + * @param E state class + */ + public static class EqualConditionListener extends ConditionListener { + public boolean check(E state) { + return state.equals(this.state); + } + } + + /** + * Not equal ConditionListener implementation + * @param E state class + */ + public static class NotEqualConditionListener extends ConditionListener { + public boolean check(E state) { + return !state.equals(this.state); + } + } + + /** + * EventListener class, which is a part of + * EventConditionListener, and implements notificatation logic, + * when condition becomes true. + */ + public static class EventListener { + public Object notificationObject; + + public void set(Object notificationObject) { + this.notificationObject = notificationObject; + } + + public void notifyEvent() { + notifyListener(notificationObject); + } + + protected static void notifyListener(Object listener) { + if (listener instanceof CountDownLatch) { + ((CountDownLatch) listener).countDown(); + } else if (listener instanceof Callable) { + try { + ((Callable) listener).call(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } else if (listener instanceof Runnable) { + ((Runnable) listener).run(); + } else { + synchronized(listener) { + listener.notify(); + } + } + } + } +} Index: util/SupportStateHolder.java --- util/SupportStateHolder.java Locally New +++ util/SupportStateHolder.java Locally New @@ -0,0 +1,37 @@ +/* + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the license at + * https://glassfish.dev.java.net/public/CDDLv1.0.html or + * glassfish/bootstrap/legal/CDDLv1.0.txt. + * See the License for the specific language governing + * permissions and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at glassfish/bootstrap/legal/CDDLv1.0.txt. + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * you own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + */ + +package com.sun.grizzly.util; + +/** + * Interface implementors support StateHolder for state control + * + * @author Alexey Stashok + */ +public interface SupportStateHolder { + /** + * Gets StateHolder for this object + * @return StateHolder + */ + public StateHolder getStateHolder(); +}