/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.grinder; import net.grinder.common.GrinderException; import net.grinder.common.GrinderProperties; import net.grinder.communication.CommunicationDefaults; import net.grinder.engine.agent.Agent; import net.grinder.engine.agent.AgentImplementationEx; import net.grinder.util.ListenerSupport; import net.grinder.util.ListenerSupport.Informer; import org.apache.commons.lang.StringUtils; import org.ngrinder.common.util.ThreadUtils; import org.ngrinder.infra.AgentConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.ngrinder.common.util.ExceptionUtils.processException; /** * Agent Daemon wrapper for {@link AgentImplementationEx} in thread. * * @author JunHo Yoon * @since 3.0 */ public class AgentDaemon implements Agent { private volatile AgentImplementationEx agent; private Thread thread = new Thread(); private GrinderProperties properties; private final ListenerSupport<AgentShutDownListener> m_listeners = new ListenerSupport<AgentShutDownListener>(); private boolean forceShutdown = false; public static final Logger LOGGER = LoggerFactory.getLogger("agent daemon"); private final AgentConfig m_agentConfig; /** * Constructor. * * @param agentConfig agent configuration */ public AgentDaemon(AgentConfig agentConfig) { this.m_agentConfig = agentConfig; try { properties = new GrinderProperties(GrinderProperties.DEFAULT_PROPERTIES); } catch (GrinderException e) { throw processException("Exception occurred while creating agent daemon", e); } } /** * Set agent. * * @param agent agent * @return set agent */ public synchronized AgentImplementationEx setAgent(AgentImplementationEx agent) { this.agent = agent; return this.agent; } /** * Run agent to connect to default console in localhost. */ public void run() { run(null, CommunicationDefaults.CONSOLE_PORT); } /** * Run agent to connect to given console in localhost. * * @param consolePort port to which agent connect */ public void run(int consolePort) { run(null, consolePort); } /** * Run agent with given {@link GrinderProperties}. * * @param grinderProperties {@link GrinderProperties} */ public void run(GrinderProperties grinderProperties) { this.properties = grinderProperties; run(null, 0); } /** * Run agent with given consoleHost and consolePort. * * if consoleHost is null it will use localhost or use console host set in * {@link GrinderProperties} * * if port number is 0, it will use default consolePort or use console port * set in {@link GrinderProperties} * * @param consoleHost host name * @param consolePort port number */ public void run(String consoleHost, int consolePort) { if (StringUtils.isNotEmpty(consoleHost)) { getGrinderProperties().setProperty(GrinderProperties.CONSOLE_HOST, consoleHost); } if (consolePort > 0) { getGrinderProperties().setInt(GrinderProperties.CONSOLE_PORT, consolePort); } thread = new Thread(new AgentThreadRunnable(), "Agent daemon connecting to port " + getGrinderProperties().getInt(GrinderProperties.CONSOLE_PORT, 0)); thread.setDaemon(true); thread.start(); LOGGER.info("{} is started.", thread.getName()); } private GrinderProperties getGrinderProperties() { return this.properties; } class AgentThreadRunnable implements Runnable { public void run() { try { setAgent(new AgentImplementationEx(LOGGER, m_agentConfig)).run(getGrinderProperties()); } catch (Exception e) { LOGGER.error("While running an agent thread, an error occurred", e); } getListeners().apply(new Informer<AgentShutDownListener>() { public void inform(AgentShutDownListener listener) { listener.shutdownAgent(); } }); if (isForceShutdown()) { setForceShutdown(false); } } } /** * Interface to detect agent shutdown. * * @author JunHo Yoon */ public interface AgentShutDownListener { /** * AgentShutdown listening method. */ public void shutdownAgent(); } public ListenerSupport<AgentShutDownListener> getListeners() { return this.m_listeners; } /** * Reset all shutdown listener. */ public void resetListeners() { final ListenerSupport<AgentShutDownListener> backup = new ListenerSupport<AgentDaemon.AgentShutDownListener>(); getListeners().apply(new Informer<AgentShutDownListener>() { public void inform(AgentShutDownListener listener) { backup.add(listener); } }); backup.apply(new Informer<AgentShutDownListener>() { public void inform(AgentShutDownListener listener) { getListeners().remove(listener); } }); } /** * Add AgentShutdownListener. * * @param listener listener to detect to Agent Shutdown */ public void addListener(AgentShutDownListener listener) { m_listeners.add(listener); } /** * Shutdown. */ public void shutdown() { try { forceShutdown = true; if (agent != null) { agent.shutdown(); } ThreadUtils.stopQuietly(thread, "Agent daemon is not stopped. So stop by force"); thread = null; } catch (Exception e) { throw processException("Exception occurred while shutting down the agent daemon", e); } } private boolean isForceShutdown() { return forceShutdown; } private void setForceShutdown(boolean force) { this.forceShutdown = force; } }