/** * Equality. * * @param other Object to compare. * @return <code>true</code> if and only if we're equal to <code>other</code>. */ public boolean equals(Object other) { if (this == other) { return true; } if (other == null || other.getClass() != ScriptLocation.class) { return false; } final ScriptLocation otherScriptLocation = (ScriptLocation) other; return getDirectory().equals(otherScriptLocation.getDirectory()) && getFile().equals(otherScriptLocation.getFile()); }
/** {@inheritDoc} */ @Override public ScriptEngine createScriptEngine(ScriptLocation script) throws EngineException { if (m_groovyFileMatcher.accept(script.getFile())) { return new GroovyScriptEngine(script); } return null; }
/** * Constructor. * * @param properties Properties. * @param dcrContext DCR context. * @param scriptLocation Script location. */ public GroovyScriptEngineService( GrinderProperties properties, // DCRContext dcrContext, ScriptLocation scriptLocation) { // This property name is poor, since it really means "If DCR // instrumentation is available, avoid the traditional Jython // instrumenter". I'm not renaming it, since I expect it only to last // a few releases, until DCR becomes the default. m_forceDCRInstrumentation = properties.getBoolean("grinder.dcrinstrumentation", false) // Hack: force DCR instrumentation for non-Jython scripts. || m_groovyFileMatcher.accept(scriptLocation.getFile()); m_dcrContext = dcrContext; }
/** * 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); } }
/** * Constructor for JythonScriptEngine. * * @param pySystemState Python system state. * @throws EngineException If the script engine could not be created. */ public JythonScriptEngine(final ScriptLocation script) throws EngineException { // Work around Jython issue 1894900. // If the python.cachedir has not been specified, and Jython is loaded // via the manifest classpath or the jar in the lib directory is // explicitly mentioned in the CLASSPATH, then set the cache directory to // be alongside jython.jar. if (System.getProperty(PYTHON_HOME) == null && System.getProperty(PYTHON_CACHEDIR) == null) { final String classpath = System.getProperty("java.class.path"); final File grinderJar = findFileInPath(classpath, "grinder.jar"); final File grinderJarDirectory = grinderJar != null ? grinderJar.getParentFile() : new File("."); final File jythonJar = findFileInPath(classpath, "jython.jar"); final File jythonHome = jythonJar != null ? jythonJar.getParentFile() : grinderJarDirectory; if (grinderJarDirectory == null && jythonJar == null || grinderJarDirectory != null && grinderJarDirectory.equals(jythonHome)) { final File cacheDir = new File(jythonHome, CACHEDIR_DEFAULT_NAME); System.setProperty("python.cachedir", cacheDir.getAbsolutePath()); } } m_systemState = new PySystemState(); m_interpreter = new PythonInterpreter(null, m_systemState); m_interpreter.exec("class ___DieQuietly___: pass"); m_dieQuietly = (PyClass) m_interpreter.get("___DieQuietly___"); String version; try { version = PySystemState.class.getField("version").get(null).toString(); } catch (final Exception e) { version = "Unknown"; } m_version = version; // Prepend the script directory to the Python path. This matches the // behaviour of the Jython interpreter. m_systemState.path.insert(0, new PyString(script.getFile().getParent())); // Additionally, add the working directory to the Python path. I think // this will always be the same as the worker's CWD. Users expect to be // able to import from the directory the agent is running in or (when the // script has been distributed), the distribution directory. m_systemState.path.insert(1, new PyString(script.getDirectory().getFile().getPath())); try { // Run the test script, script does global set up here. m_interpreter.execfile(script.getFile().getPath()); } catch (final PyException e) { throw new JythonScriptExecutionException("initialising test script", e); } // Find the callable that acts as a factory for test runner instances. m_testRunnerFactory = m_interpreter.get(TEST_RUNNER_CALLABLE_NAME); if (m_testRunnerFactory == null || !m_testRunnerFactory.isCallable()) { throw new JythonScriptExecutionException( "There is no callable (class or function) named '" + TEST_RUNNER_CALLABLE_NAME + "' in " + script); } }