Package net.grinder

Source Code of net.grinder.AgentController$ConsoleCommunication

/*
* 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.AgentDaemon.AgentShutDownListener;
import net.grinder.common.GrinderException;
import net.grinder.common.GrinderProperties;
import net.grinder.communication.*;
import net.grinder.engine.agent.Agent;
import net.grinder.engine.common.AgentControllerConnectorFactory;
import net.grinder.engine.communication.AgentControllerServerListener;
import net.grinder.engine.communication.AgentDownloadGrinderMessage;
import net.grinder.engine.communication.AgentUpdateGrinderMessage;
import net.grinder.engine.communication.LogReportGrinderMessage;
import net.grinder.engine.controller.AgentControllerIdentityImplementation;
import net.grinder.message.console.AgentControllerProcessReportMessage;
import net.grinder.message.console.AgentControllerState;
import net.grinder.messages.agent.StartGrinderMessage;
import net.grinder.messages.console.AgentAddress;
import net.grinder.util.LogCompressUtils;
import net.grinder.util.NetworkUtils;
import net.grinder.util.thread.Condition;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.ngrinder.common.constants.AgentConstants;
import org.ngrinder.infra.AgentConfig;
import org.ngrinder.monitor.collector.SystemDataCollector;
import org.ngrinder.monitor.controller.model.SystemDataModel;
import org.ngrinder.monitor.share.domain.SystemInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FilenameFilter;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;

import static org.ngrinder.common.constants.InternalConstants.PROP_INTERNAL_NGRINDER_VERSION;
import static org.ngrinder.common.util.NoOp.noOp;
import static org.ngrinder.common.util.Preconditions.checkNotNull;

/**
* Agent Controller which handles agent start and stop.
*
* @author JunHo Yoon
* @since 3.0
*/
public class AgentController implements Agent, AgentConstants {

  private static final Logger LOGGER = LoggerFactory.getLogger("agent controller");
  private final AgentConfig agentConfig;

  private Timer m_timer;
  @SuppressWarnings("FieldCanBeLocal")
  private final Condition m_eventSynchronisation = new Condition();
  private final AgentControllerIdentityImplementation m_agentIdentity;
  private final AgentControllerServerListener m_agentControllerServerListener;
  private FanOutStreamSender m_fanOutStreamSender;
  private final AgentControllerConnectorFactory m_connectorFactory = new AgentControllerConnectorFactory(
      ConnectionType.AGENT);
  private final Condition m_eventSyncCondition;
  private volatile AgentControllerState m_state = AgentControllerState.STARTED;

  private SystemDataCollector agentSystemDataCollector = new SystemDataCollector();

  private int m_connectionPort = 0;

  private static SystemDataModel emptySystemDataModel = new SystemDataModel();

  private AgentUpdateHandler agentUpdateHandler;

  private int retryCount = 0;

  private String version;

  /**
   * Constructor.
   *
   * @param eventSyncCondition event sync condition to wait until agent start to run.
   */
  public AgentController(Condition eventSyncCondition, AgentConfig agentConfig) throws GrinderException {
    this.m_eventSyncCondition = eventSyncCondition;

    this.agentConfig = agentConfig;
    this.version = agentConfig.getInternalProperties().getProperty(PROP_INTERNAL_NGRINDER_VERSION);
    this.m_agentControllerServerListener = new AgentControllerServerListener(m_eventSynchronisation, LOGGER);
    // Set it with the default name
    this.m_agentIdentity = new AgentControllerIdentityImplementation(agentConfig.getAgentHostID(), NetworkUtils.DEFAULT_LOCAL_HOST_ADDRESS);
    this.m_agentIdentity.setRegion(agentConfig.getRegion());
    this.agentSystemDataCollector = new SystemDataCollector();
    this.agentSystemDataCollector.setAgentHome(agentConfig.getHome().getDirectory());
    this.agentSystemDataCollector.refresh();
  }


  /**
   * Run the agent controller.
   *
   * @throws GrinderException occurs when the test execution is failed.
   */
  @SuppressWarnings("ConstantConditions")
  public void run() throws GrinderException {
    synchronized (m_eventSyncCondition) {
      m_eventSyncCondition.notifyAll();
    }

    StartGrinderMessage startMessage = null;
    ConsoleCommunication consoleCommunication = null;
    m_fanOutStreamSender = new FanOutStreamSender(GrinderConstants.AGENT_CONTROLLER_FANOUT_STREAM_THREAD_COUNT);
    m_timer = new Timer(false);
    AgentDaemon agent = new AgentDaemon(checkNotNull(agentConfig,
        "agent.conf should be provided before agent daemon start."));
    try {
      while (true) {
        do {
          if (consoleCommunication == null) {
            final Connector connector = m_connectorFactory.create(agentConfig.getControllerIP(), agentConfig.getControllerPort());
            try {
              consoleCommunication = new ConsoleCommunication(connector);
              consoleCommunication.start();
              LOGGER.info("Connected to agent controller server at {}", connector.getEndpointAsString());
            } catch (CommunicationException e) {
              LOGGER.error("Error while connecting to agent controller server at {}",
                  connector.getEndpointAsString());
              return;
            }
          }

          if (consoleCommunication != null && startMessage == null) {
            if (m_state == AgentControllerState.UPDATING) {
              m_agentControllerServerListener.waitForMessage();
              break;
            } else {
              LOGGER.info("Waiting for agent controller server signal");
              m_state = AgentControllerState.READY;
              m_agentControllerServerListener.waitForMessage();
              if (m_agentControllerServerListener.received(AgentControllerServerListener.START)) {
                startMessage = m_agentControllerServerListener.getLastStartGrinderMessage();
                LOGGER.info("Agent start message is received from controller {}", startMessage);
                continue;
              } else {
                break; // Another message, check at end of outer
                // while loop.
              }
            }
          }

          if (startMessage != null) {
            m_agentIdentity.setNumber(startMessage.getAgentNumber());
          }
        } while (checkNotNull(startMessage).getProperties() == null);

        // Here the agent run code goes..
        if (startMessage != null) {
          final String testId = startMessage.getProperties().getProperty("grinder.test.id", "unknown");
          LOGGER.info("Starting agent... for {}", testId);
          m_state = AgentControllerState.BUSY;
          m_connectionPort = startMessage.getProperties().getInt(GrinderProperties.CONSOLE_PORT, 0);
          agent.run(startMessage.getProperties());

          final ConsoleCommunication conCom = consoleCommunication;
          agent.resetListeners();
          agent.addListener(new AgentShutDownListener() {
            @Override
            public void shutdownAgent() {
              LOGGER.info("Send log for {}", testId);
              sendLog(conCom, testId);
              m_state = AgentControllerState.READY;
              m_connectionPort = 0;
            }
          });
        }
        // Ignore any pending start messages.
        m_agentControllerServerListener.discardMessages(AgentControllerServerListener.START);

        if (!m_agentControllerServerListener.received(AgentControllerServerListener.ANY)) {
          // We've got here naturally, without a console signal.
          LOGGER.info("Agent is started. Waiting for agent controller signal");
          m_agentControllerServerListener.waitForMessage();
        }

        if (m_agentControllerServerListener.received(AgentControllerServerListener.START)) {
          startMessage = m_agentControllerServerListener.getLastStartGrinderMessage();
        } else if (m_agentControllerServerListener.received(AgentControllerServerListener.STOP)) {
          agent.shutdown();
          startMessage = null;
          m_connectionPort = 0;
          m_agentControllerServerListener.discardMessages(AgentControllerServerListener.STOP);
        } else if (m_agentControllerServerListener.received(AgentControllerServerListener.SHUTDOWN)) {
          m_connectionPort = 0;
          break;
        } else if (m_agentControllerServerListener.received(AgentControllerServerListener.AGENT_UPDATE)) {
          // Do update agent by downloading new version.
          startMessage = null;
          m_connectionPort = 0;
          m_state = AgentControllerState.UPDATING;
          final AgentUpdateGrinderMessage message = m_agentControllerServerListener.getLastAgentUpdateGrinderMessage();
          m_agentControllerServerListener.discardMessages(AgentControllerServerListener.AGENT_UPDATE);
          AgentDownloadGrinderMessage agentDownloadGrinderMessage = new AgentDownloadGrinderMessage(message.getVersion());
          try {
            // If it's initial message
            if (agentUpdateHandler == null && message.getNext() == 0) {
              IOUtils.closeQuietly(agentUpdateHandler);
              agentUpdateHandler = new AgentUpdateHandler(agentConfig, message);
            } else if (agentUpdateHandler != null) {
              if (message.isValid()) {
                retryCount = 0;
                agentUpdateHandler.update(message);
                agentDownloadGrinderMessage.setNext(message.getNext());
              } else if (retryCount <= AgentDownloadGrinderMessage.MAX_RETRY_COUNT) {
                retryCount++;
                agentDownloadGrinderMessage.setNext(message.getOffset());
              } else {
                throw new CommunicationException("Error while getting the agent package from " +
                    "controller");
              }
            } else {
              throw new CommunicationException("Error while getting the agent package from controller");
            }
            if (consoleCommunication != null) {
              consoleCommunication.sendMessage(agentDownloadGrinderMessage);
            } else {
              break;
            }

          } catch (IllegalArgumentException ex) {
            IOUtils.closeQuietly(agentUpdateHandler);
            agentUpdateHandler = null;
            retryCount = 0;
            LOGGER.info("same or old agent version {} is sent for update. skip this.",
                message.getVersion());
            m_state = AgentControllerState.READY;
          } catch (Exception e) {
            retryCount = 0;
            IOUtils.closeQuietly(agentUpdateHandler);
            agentUpdateHandler = null;
            LOGGER.error("While updating agent, the exception occurred.", e);
            m_state = AgentControllerState.READY;
          }

        } else {
          // ConsoleListener.RESET or natural death.
          startMessage = null;
        }
      }

    } finally {
      m_connectionPort = 0;
      // Abnormal state.
      agent.shutdown();
      m_state = AgentControllerState.FINISHED;
      shutdownConsoleCommunication(consoleCommunication);
      m_timer.cancel();
    }
  }

  private void sendLog(ConsoleCommunication consoleCommunication, String testId) {
    File logFolder = new File(agentConfig.getHome().getLogDirectory(), testId);
    if (!logFolder.exists()) {
      return;
    }
    File[] logFiles = logFolder.listFiles(new FilenameFilter() {
      @Override
      public boolean accept(File dir, String name) {
        return (name.endsWith(".log"));
      }
    });

    if (logFiles == null || ArrayUtils.isEmpty(logFiles)) {
      LOGGER.error("No log exists under {}", logFolder.getAbsolutePath());
      return;
    }
    Arrays.sort(logFiles);
    // Take only one file... if agent.send.all.logs is not set.
    if (!agentConfig.getAgentProperties().getPropertyBoolean(PROP_AGENT_ALL_LOGS)) {
      logFiles = new File[]{logFiles[0]};
    }
    final byte[] compressedLog = LogCompressUtils.compress(logFiles,
        Charset.defaultCharset(), Charset.forName("UTF-8")
    );
    consoleCommunication.sendMessage(new LogReportGrinderMessage(testId, compressedLog, new AgentAddress(m_agentIdentity)));
    // Delete logs to clean up
    if (!agentConfig.getAgentProperties().getPropertyBoolean(PROP_AGENT_KEEP_LOGS)) {
      LOGGER.info("Clean up the perftest logs");
      FileUtils.deleteQuietly(logFolder);
    }
  }

  private void shutdownConsoleCommunication(ConsoleCommunication consoleCommunication) {
    sendCurrentState(consoleCommunication);
    if (consoleCommunication != null) {
      consoleCommunication.shutdown();
      //noinspection UnusedAssignment
      consoleCommunication = null;
    }
    m_agentControllerServerListener.discardMessages(AgentControllerServerListener.ANY);
  }

  private void sendCurrentState(ConsoleCommunication consoleCommunication) {
    if (consoleCommunication != null) {
      try {
        consoleCommunication.sendCurrentState();
      } catch (CommunicationException e) {
        LOGGER.error("Error while sending current state : {}.", e.getMessage());
        LOGGER.debug("The error detail is ", e);
      }
    }
  }

  /**
   * Clean up resources.
   */
  public void shutdown() {
    if (m_timer != null) {
      m_timer.cancel();
    }
    if (m_fanOutStreamSender != null) {
      m_fanOutStreamSender.shutdown();
    }
    m_agentControllerServerListener.shutdown();
    LOGGER.info("Agent controller shuts down");
  }

  /**
   * Get current System performance.
   *
   * @return {@link SystemDataModel} instance
   */
  public SystemDataModel getSystemDataModel() {
    try {
      SystemInfo systemInfo = agentSystemDataCollector.execute();
      return new SystemDataModel(systemInfo, this.version);
    } catch (Exception e) {
      LOGGER.error("Error while getting system data model : {} ", e.getMessage());
      LOGGER.debug("The error detail is ", e);
      return emptySystemDataModel;
    }
  }

  public AgentConfig getAgentConfig() {
    return agentConfig;
  }


  public final class ConsoleCommunication {
    private final ClientSender m_sender;
    private final TimerTask m_reportRunningTask;
    private final MessagePump m_messagePump;

    public ConsoleCommunication(Connector connector) throws CommunicationException {
      final ClientReceiver receiver = ClientReceiver.connect(connector, new AgentAddress(m_agentIdentity));
      m_sender = ClientSender.connect(receiver);

      m_sender.send(new AgentControllerProcessReportMessage(AgentControllerState.STARTED, getSystemDataModel(),
          m_connectionPort, version));
      final MessageDispatchSender messageDispatcher = new MessageDispatchSender();
      m_agentControllerServerListener.registerMessageHandlers(messageDispatcher);

      m_messagePump = new MessagePump(receiver, messageDispatcher, 1);

      m_reportRunningTask = new TimerTask() {
        public void run() {
          try {
            sendCurrentState();
          } catch (CommunicationException e) {
            cancel();
            LOGGER.error("Error while sending current state:" + e.getMessage());
            LOGGER.debug("The error detail is", e);
          }
        }
      };
    }

    public void sendMessage(Message message) {
      try {
        m_sender.send(message);
      } catch (CommunicationException e) {
        LOGGER.error("{}. This error is not critical if it doesn't occur much.", e.getMessage());
      }
    }

    public void sendCurrentState() throws CommunicationException {
      sendMessage(new AgentControllerProcessReportMessage(m_state, getSystemDataModel(), m_connectionPort, version));
    }

    public void start() {
      m_messagePump.start();
      m_timer.schedule(m_reportRunningTask, 0, GrinderConstants.AGENT_CONTROLLER_HEARTBEAT_INTERVAL);
    }

    public void shutdown() {
      m_reportRunningTask.cancel();
      try {
        m_sender.send(new AgentControllerProcessReportMessage(AgentControllerState.FINISHED, null, 0, version));
      } catch (CommunicationException e) {
        // Fall through
        // Ignore - peer has probably shut down.
        noOp();
      } finally {
        m_messagePump.shutdown();
      }
    }

  }
}
TOP

Related Classes of net.grinder.AgentController$ConsoleCommunication

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.