/** * Constructor. * * @param translations Translation service. * @param processControl Console process control. * @param sampleModel Console sample model. * @param logger Logger. */ public TextUI( final Translations translations, final ProcessControl processControl, final SampleModel sampleModel, final Logger logger) { m_logger = logger; m_logger.info(GrinderBuild.getName()); m_shutdownHook = new Thread(new ShutdownHook(translations)); Runtime.getRuntime().addShutdownHook(m_shutdownHook); m_errorHandler = new ErrorHandlerImplementation(); processControl.addProcessStatusListener(new ProcessListener(translations)); m_sampleModel = sampleModel; m_sampleModel.addModelListener( new SampleModel.AbstractListener() { @Override public void stateChanged() { m_logger.info(m_sampleModel.getState().getDescription()); } }); }
/** * Run the Grinder agent process. * * @throws GrinderException If an error occurs. */ public void run() throws GrinderException { StartGrinderMessage startMessage = null; ConsoleCommunication consoleCommunication = null; try { while (true) { m_logger.info(GrinderBuild.getName()); ScriptLocation script = null; GrinderProperties properties; do { properties = createAndMergeProperties(startMessage != null ? startMessage.getProperties() : null); m_agentIdentity.setName(properties.getProperty("grinder.hostID", getHostName())); final Connector connector = properties.getBoolean("grinder.useConsole", true) ? m_connectorFactory.create(properties) : null; // We only reconnect if the connection details have changed. if (consoleCommunication != null && !consoleCommunication.getConnector().equals(connector)) { shutdownConsoleCommunication(consoleCommunication); consoleCommunication = null; // Accept any startMessage from previous console - see bug 2092881. } if (consoleCommunication == null && connector != null) { try { consoleCommunication = new ConsoleCommunication(connector); consoleCommunication.start(); m_logger.info("connected to console at {}", connector.getEndpointAsString()); } catch (CommunicationException e) { if (m_proceedWithoutConsole) { m_logger.warn( "{}, proceeding without the console; set " + "grinder.useConsole=false to disable this warning.", e.getMessage()); } else { m_logger.error(e.getMessage()); return; } } } if (consoleCommunication != null && startMessage == null) { m_logger.info("waiting for console signal"); m_consoleListener.waitForMessage(); if (m_consoleListener.received(ConsoleListener.START)) { startMessage = m_consoleListener.getLastStartGrinderMessage(); continue; // Loop to handle new properties. } else { break; // Another message, check at end of outer while loop. } } if (startMessage != null) { final GrinderProperties messageProperties = startMessage.getProperties(); final Directory fileStoreDirectory = m_fileStore.getDirectory(); // Convert relative path to absolute path. messageProperties.setAssociatedFile( fileStoreDirectory.getFile(messageProperties.getAssociatedFile())); final File consoleScript = messageProperties.resolveRelativeFile( messageProperties.getFile( GrinderProperties.SCRIPT, GrinderProperties.DEFAULT_SCRIPT)); // We only fall back to the agent properties if the start message // doesn't specify a script and there is no default script. if (messageProperties.containsKey(GrinderProperties.SCRIPT) || consoleScript.canRead()) { // The script directory may not be the file's direct parent. script = new ScriptLocation(fileStoreDirectory, consoleScript); } m_agentIdentity.setNumber(startMessage.getAgentNumber()); } else { m_agentIdentity.setNumber(-1); } if (script == null) { final File scriptFile = properties.resolveRelativeFile( properties.getFile(GrinderProperties.SCRIPT, GrinderProperties.DEFAULT_SCRIPT)); script = new ScriptLocation(scriptFile); } if (!script.getFile().canRead()) { m_logger.error("The script file '" + script + "' does not exist or is not readable."); script = null; break; } } while (script == null); if (script != null) { final String jvmArguments = properties.getProperty("grinder.jvm.arguments"); final WorkerFactory workerFactory; if (!properties.getBoolean("grinder.debug.singleprocess", false)) { final WorkerProcessCommandLine workerCommandLine = new WorkerProcessCommandLine( properties, System.getProperties(), jvmArguments, script.getDirectory()); m_logger.info("Worker process command line: {}", workerCommandLine); workerFactory = new ProcessWorkerFactory( workerCommandLine, m_agentIdentity, m_fanOutStreamSender, consoleCommunication != null, script, properties); } else { m_logger.info("DEBUG MODE: Spawning threads rather than processes"); if (jvmArguments != null) { m_logger.warn( "grinder.jvm.arguments ({}) ignored in single process mode", jvmArguments); } workerFactory = new DebugThreadWorkerFactory( m_agentIdentity, m_fanOutStreamSender, consoleCommunication != null, script, properties); } final WorkerLauncher workerLauncher = new WorkerLauncher( properties.getInt("grinder.processes", 1), workerFactory, m_eventSynchronisation, m_logger); final int increment = properties.getInt("grinder.processIncrement", 0); if (increment > 0) { final boolean moreProcessesToStart = workerLauncher.startSomeWorkers( properties.getInt("grinder.initialProcesses", increment)); if (moreProcessesToStart) { final int incrementInterval = properties.getInt("grinder.processIncrementInterval", 60000); final RampUpTimerTask rampUpTimerTask = new RampUpTimerTask(workerLauncher, increment); m_timer.scheduleAtFixedRate(rampUpTimerTask, incrementInterval, incrementInterval); } } else { workerLauncher.startAllWorkers(); } // Wait for a termination event. synchronized (m_eventSynchronisation) { final long maximumShutdownTime = 20000; long consoleSignalTime = -1; while (!workerLauncher.allFinished()) { if (consoleSignalTime == -1 && m_consoleListener.checkForMessage( ConsoleListener.ANY ^ ConsoleListener.START)) { workerLauncher.dontStartAnyMore(); consoleSignalTime = System.currentTimeMillis(); } if (consoleSignalTime >= 0 && System.currentTimeMillis() - consoleSignalTime > maximumShutdownTime) { m_logger.info("forcibly terminating unresponsive processes"); // destroyAllWorkers() prevents further workers from starting. workerLauncher.destroyAllWorkers(); } m_eventSynchronisation.waitNoInterrruptException(maximumShutdownTime); } } workerLauncher.shutdown(); } if (consoleCommunication == null) { break; } else { // Ignore any pending start messages. m_consoleListener.discardMessages(ConsoleListener.START); if (!m_consoleListener.received(ConsoleListener.ANY)) { // We've got here naturally, without a console signal. m_logger.info("finished, waiting for console signal"); m_consoleListener.waitForMessage(); } if (m_consoleListener.received(ConsoleListener.START)) { startMessage = m_consoleListener.getLastStartGrinderMessage(); } else if (m_consoleListener.received(ConsoleListener.STOP | ConsoleListener.SHUTDOWN)) { break; } else { // ConsoleListener.RESET or natural death. startMessage = null; } } } } finally { shutdownConsoleCommunication(consoleCommunication); } }