// Copyright (C) 2000 - 2012 Philip Aston // All rights reserved. // // This file is part of The Grinder software distribution. Refer to // the file LICENSE which is part of The Grinder distribution for // licensing details. The Grinder distribution is available on the // Internet at http://grinder.sourceforge.net/ // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. package net.grinder.engine.communication; import net.grinder.communication.Message; import net.grinder.communication.MessageDispatchRegistry; import net.grinder.communication.MessageDispatchRegistry.Handler; import net.grinder.messages.agent.StartGrinderMessage; import net.grinder.messages.agent.StopGrinderMessage; import net.grinder.util.thread.Condition; import org.slf4j.Logger; /** * Agent control messages and allows them to be asynchronously queried. * * @author Grinder Developers. * @author JunHo Yoon (modified for nGrinder) * @since 3.0 */ public final class AgentControllerServerListener { /** * Constant that represents start message. * * @see #received */ public static final int START = 1; /** * Constant that represents a a reset message. * * @see #received */ public static final int RESET = 1 << 1; /** * Constant that represents a stop message. * * @see #received */ public static final int STOP = 1 << 2; /** * Constant that represents a communication shutdown. * * @see #received */ public static final int SHUTDOWN = 1 << 3; /** * Constant that represents a agent update. * * @see #received */ public static final int AGENT_UPDATE = 1 << 4; /** * Constant that represent any message. * * @see #received */ public static final int ANY = START | RESET | STOP | SHUTDOWN | AGENT_UPDATE; private final Condition m_notifyOnMessage; private final Logger m_logger; private int m_messagesReceived = 0; private int m_lastMessagesReceived = 0; private StartGrinderMessage m_lastStartGrinderMessage; private AgentUpdateGrinderMessage m_lastAgentUpdateGrinderMessage; /** * Constructor. * * @param notifyOnMessage An <code>Object</code> to notify when a message arrives. * @param logger logger to log received event messages to. */ public AgentControllerServerListener(Condition notifyOnMessage, Logger logger) { m_notifyOnMessage = notifyOnMessage; m_logger = logger; } /** * Shut down. */ public void shutdown() { setReceived(SHUTDOWN); } /** * Wait until any message is received. * <p/> * <p> * After calling this method, the actual messages can be determined using {@link #received}. * </p> */ public void waitForMessage() { while (!checkForMessage(AgentControllerServerListener.ANY)) { synchronized (m_notifyOnMessage) { m_notifyOnMessage.waitNoInterrruptException(); } } } /** * Check for messages matching the given mask. * <p/> * <p> * After calling this method, the actual messages can be determined using {@link #received}. * </p> * * @param mask The messages to check for. * @return <code>true</code> if at least one message matches the <code>mask</code> parameter has * been received since the last time the message was checked for, or if communications * have been shutdown. <code>false</code> otherwise. */ public boolean checkForMessage(int mask) { synchronized (this) { final int intersection = m_messagesReceived & mask; try { m_lastMessagesReceived = intersection; } finally { m_messagesReceived ^= intersection; } } return received(mask | SHUTDOWN); } /** * Discard pending messages that match the given mask. * * @param mask The messages to discard. */ public void discardMessages(int mask) { synchronized (this) { m_lastMessagesReceived &= ~mask; m_messagesReceived &= ~mask; } } /** * Query the messages set up by the last {@link #checkForMessage} or {@link #waitForMessage} * call. * * @param mask The messages to check for. * @return <code>true</code> if one or more of the received messages matches <code>mask</code>. */ public synchronized boolean received(int mask) { return (m_lastMessagesReceived & mask) != 0; } private void setReceived(int message) { synchronized (this) { m_messagesReceived |= message; } synchronized (m_notifyOnMessage) { m_notifyOnMessage.notifyAll(); } } /** * Registers message handlers with a dispatcher. * * @param messageDispatcher The dispatcher. */ public void registerMessageHandlers(MessageDispatchRegistry messageDispatcher) { messageDispatcher.set(StartGrinderMessage.class, new AbstractMessageHandler<StartGrinderMessage>() { public void handle(StartGrinderMessage message) { m_logger.info("Received a start agent message"); m_lastStartGrinderMessage = message; setReceived(START); } }); messageDispatcher.set(StopGrinderMessage.class, new AbstractMessageHandler<StopGrinderMessage>() { public void handle(StopGrinderMessage message) { m_logger.info("Received a stop agent message"); setReceived(STOP); } }); messageDispatcher.set(AgentUpdateGrinderMessage.class, new AbstractMessageHandler<AgentUpdateGrinderMessage>() { public void handle(AgentUpdateGrinderMessage message) { m_logger.info("Received a agent update message {}", message.getNext()); m_lastAgentUpdateGrinderMessage = message; setReceived(AGENT_UPDATE); } }); } /** * Return the last {@link StartGrinderMessage} received. * * @return The message. */ public StartGrinderMessage getLastStartGrinderMessage() { return m_lastStartGrinderMessage; } private abstract class AbstractMessageHandler<T extends Message> implements Handler<T> { public void shutdown() { final boolean shutdown; synchronized (AgentControllerServerListener.this) { shutdown = (m_messagesReceived & SHUTDOWN) == 0; } if (shutdown) { m_logger.info("agent controller communication is shutdown"); setReceived(SHUTDOWN); } } } public AgentUpdateGrinderMessage getLastAgentUpdateGrinderMessage() { return m_lastAgentUpdateGrinderMessage; } }