Public/JMeter/Wait-JMeter.ps1
<#
The MIT License (MIT) Copyright (c) 2015 Objectivity Bespoke Software Specialists Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #> function Wait-JMeter { <# .SYNOPSIS Waits until JMeter process spawned by Start-JMeter function finishes and validates JTL file is generated. .PARAMETER JMeterPid Process id of running JMeter. Must specify either this parameter or JMeterPidFile. .PARAMETER JMeterDir Path to root JMeter directory. .PARAMETER JMeterNonGUIPort The port where jmeter in non-GUI mode is listening on. .PARAMETER JMeterPidFile Path to a file containing running JMeter process id. Must specify either this parameter or JMeterPid. .PARAMETER JtlOutputFile Output file that will be created by JMeter (JTL). .PARAMETER StdOutFile File containing stdout generated by jmeter.bat. .PARAMETER StdErrFile File containing stderr generated by jmeter.bat. .PARAMETER TimeoutInSeconds Maximum time to wait before jmeter process is killed. .PARAMETER ShutdownMode The mode that should be used to kill JMeter process. Available options: - 'KillProcess' kills the processs explicitly - 'SendShutdownMessage' run the Shutdown client to stop a non-GUI instance gracefully - 'SendStopTestNowMessage' run the Shutdown client to stop a non-GUI instance abruptly .PARAMETER KillAfterTimeout If specified then the JMeter process is killed once $TimeoutInSeconds expires. .PARAMETER ForceKillTimeoutInSeconds Maximum time to wait before jmeter process is killed forcefully after waiting for timeout specified by TimeoutInSeconds. .OUTPUTS True if JMeter process is still running, false otherwise. .EXAMPLE Wait-JMeter -JMeterPidFile 'f:\jmeter_pid' -JtlOutputFile "c:\workspace\test.jtl" -StdOutFile "f:\jmeter-stdout.txt" -StdErrFile "f:\jmeter-stderr.txt" #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory=$false)] [int] $JMeterPid, [Parameter(Mandatory=$false)] [string] $JMeterPidFile, [Parameter(Mandatory=$false)] [string] $JMeterDir, [Parameter(Mandatory=$false)] [int] $JMeterNonGUIPort = 4445, [Parameter(Mandatory=$true)] [string] $JtlOutputFile, [Parameter(Mandatory=$false)] [string] $StdOutFile, [Parameter(Mandatory=$false)] [string] $StdErrFile, [Parameter(Mandatory=$false)] [int] $TimeoutInSeconds = 7200, [Parameter(Mandatory=$false)] [ValidateSet("KillProcess", "SendShutdownMessage", "SendStopTestNowMessage")] [string] $ShutdownMode = "KillProcess", [Parameter(Mandatory=$false)] [switch] $KillAfterTimeout, [Parameter(Mandatory=$false)] [int] $ForceKillTimeoutInSeconds = 60 ) if (!$JMeterPid -and !$JMeterPidFile) { throw 'Please specify one of $JMeterPid or $JMeterPidFile parameters.' } if (!$JMeterPid -and $JMeterPidFile) { if (!(Test-Path -LiteralPath $JMeterPidFile)) { throw "No JMeter Pid file at '$JMeterPidFile'. Please investigate why Start-JMeter has not created it." } $JMeterPid = Get-Content -Path $JMeterPidFile -ReadCount 1 } Write-Log -Info "Waiting for JMeter process (pid = $JMeterPid) to finish (timeout = $TimeoutInSeconds s)." $process = Get-Process -Id $JMeterPid -ErrorAction SilentlyContinue if (!$process) { Write-JMeterStdOutAndStdErr -StdOutFile $StdOutFile -StdErrFile $StdErrFile Test-JMeterSuccess -JtlOutputFile $JtlOutputFile return $false } Write-ProgressExternal -Message 'Waiting for JMeter' # todo: add JMeter real-time logging if (!$process.WaitForExit($TimeoutInSeconds * 1000)) { if ($KillAfterTimeout) { if ($ShutdownMode -eq "KillProcess") { Stop-ProcessForcefully -Process $process -KillTimeoutInSeconds 1 Write-ProgressExternal -Message '' throw "JMeter process has not finished after $TimeoutInSeconds s and has been killed." } elseif (!(Test-Path -LiteralPath $JMeterDir)) { Write-Log -Warn "Cannot find JMeter directory at '$JMeterDir'. JMeter process will be stopped in 'KillProcess' mode instead of '$ShutdownMode'" Stop-ProcessForcefully -Process $process -KillTimeoutInSeconds 1 Write-ProgressExternal -Message '' throw "JMeter process has not finished after $TimeoutInSeconds s and has been killed." } else { $javaPath = "java.exe" if ($ShutdownMode -eq "SendShutdownMessage") { $message = "Shutdown" } else { $message = "StopTestNow" } $apacheJMeterJarPath = Join-Path -Path $JMeterDir -ChildPath "bin\ApacheJMeter.jar" $cmdArgs += "-cp $apacheJMeterJarPath org.apache.jmeter.util.ShutdownClient $message $JMeterNonGUIPort" Write-Log -Info "JMeter process has not finished after $TimeoutInSeconds s - sending '$message' message..." [void](Start-ExternalProcess -Command $javaPath -ArgumentList $cmdArgs) # wait additional minute for shutdown if (!$process.WaitForExit($ForceKillTimeoutInSeconds * 1000)) { Write-Log -Info "JMeter process is still running after $ForceKillTimeoutInSeconds s - killing." Stop-ProcessForcefully -Process $process -KillTimeoutInSeconds $ForceKillTimeoutInSeconds } Write-Log -Info "JMeter process has been stopped." } } else { Write-ProgressExternal -Message '' Write-Log -Info "JMeter process still running after $TimeoutInSeconds s." return $true } } Write-ProgressExternal -Message '' Write-JMeterStdOutAndStdErr -StdOutFile $StdOutFile -StdErrFile $StdErrFile Test-JMeterSuccess -JtlOutputFile $JtlOutputFile return $false } function Test-JMeterSuccess { <# .SYNOPSIS Checks whether JMeter finished successfully by checking if .jtl file has been generated. .PARAMETER JtlOutputFile Output file that should be created by JMeter (JTL). .EXAMPLE Test-JMeterSuccess -JtlOutputFile $JtlOutputFile #> [CmdletBinding()] [OutputType([void])] param( [Parameter(Mandatory=$true)] [string] $JtlOutputFile ) if (Test-Path -LiteralPath $JtlOutputFile) { Write-Log -Info "JMeter process finished and generated jtl file at '$JtlOutputFile'." -Emphasize return } else { throw "JMeter process finished but not generated jtl file at '$JtlOutputFile'. Please review stdout/stderr messages which should be logged above." } } function Write-JMeterStdOutAndStdErr { <# .SYNOPSIS Writes JMeter stdout / stderr files to stdout. .PARAMETER StdOutFile File containing stdout generated by jmeter.bat. .PARAMETER StdErrFile File containing stderr generated by jmeter.bat. .EXAMPLE Write-JMeterStdOutAndStdErr -StdOutFile $StdOutFile -StdErrFile $StdErrFile #> [CmdletBinding()] [OutputType([void])] param( [Parameter(Mandatory=$false)] [string] $StdOutFile, [Parameter(Mandatory=$false)] [string] $StdErrFile ) if ($StdOutFile) { Write-Log -Info "JMeter stdout file contents ('$StdOutFile'):" -Emphasize Get-Content -Path $StdOutFile -ReadCount 0 | Foreach-Object { Write-Log -Info $_ } } if ($StdErrFile) { Write-Log -Info "JMeter stderr file contents ('$StdErrFile'):" -Emphasize Get-Content -Path $StdErrFile -ReadCount 0 | Foreach-Object { Write-Log -Info $_ } } } |