Next Practical Step
Replace legacy BeanShell samplers with JSR223 Groovy and verify that “Cache compiled script” is checked across all test elements.
Master JMeter JSR223 Groovy scripting: implicit objects (vars, props, prev, ctx), JSON slurping, HMAC hashing, caching, and load-tested recipes.
Apache JMeter provides JSR223 test elements (Samplers, PreProcessors, PostProcessors, Assertions, and Listeners) to execute custom logic during a test run. Apache Groovy is the standard scripting engine in JMeter: it compiles directly to Java bytecode, supports automatic script caching, and executes with near-native Java performance.
This guide provides a comprehensive reference to implicit script objects, compilation caching rules, and production-tested Groovy recipes for real-world load testing.
Every JSR223 test element automatically exposes several pre-configured objects in the script scope:
| Object | Class / Type | Description & Primary Use Case |
|---|---|---|
vars | org.apache.jmeter.threads.JMeterVariables | Thread-local variable store. Read and write variables for the current virtual user. |
props | java.util.Properties | Global properties shared across all threads and thread groups in the JMeter JVM instance. |
prev | org.apache.jmeter.samplers.SampleResult | Result of the previous sampler (PostProcessors, Assertions, Listeners). |
sampler | org.apache.jmeter.samplers.Sampler | The current sampler object being executed or prepared (PreProcessors, Samplers). |
ctx | org.apache.jmeter.threads.JMeterContext | Internal engine context: thread number, engine state, active thread count, and current sampler. |
log | org.slf4j.Logger | SLF4J logger instance writing to jmeter.log (e.g., log.info("msg"), log.error("err")). |
OUT | java.io.PrintStream | Standard system output (System.out). |
args | String[] | Array of script parameter tokens passed in the Parameters field. |
vars)vars holds variables specific to the executing thread. Variables created here cannot be seen by other virtual users.
// Read variableString token = vars.get("authToken")String defaultId = vars.get("userId") ?: "guest-1000"
// Write variablevars.put("orderId", "ORD-98765")vars.put("isEligible", "true")
// Store non-string objects (accessible only within the same thread)List<String> items = ["item_A", "item_B", "item_C"]vars.putObject("userCart", items)
// Retrieve stored objectList<String> cart = (List<String>) vars.getObject("userCart")props)props is a thread-safe java.util.Properties instance visible across all threads, Thread Groups, and setUp/tearDown Thread Groups.
// Read property with a fallback defaultString apiHost = props.getProperty("app.host", "api.example.com")
// Write a property across thread groupsprops.put("sharedAuthKey", "key-live-99212")
// Synchronized counter / token rotationsynchronized (props) { int currentCounter = Integer.parseInt(props.getProperty("globalCounter", "0")) props.put("globalCounter", String.valueOf(currentCounter + 1))}prev)Available in JSR223 PostProcessor, JSR223 Assertion, and JSR223 Listener to inspect or modify response metadata:
// Read response status and bodyint responseCode = prev.getResponseCode().toInteger()String responseBody = prev.getResponseDataAsString()long latencyMs = prev.getLatency()long responseTime = prev.getTime()
// Modify sample result dynamicallyif (responseBody.contains("SESSION_EXPIRED")) { prev.setSuccessful(false) prev.setResponseCode("401") prev.setResponseMessage("Session expired detected in payload")}
// Ignore sampler from final dashboard / metrics if neededif (prev.getSampleLabel().startsWith("HealthCheck")) { prev.setIgnore()}import groovy.json.JsonSlurper
String response = prev.getResponseDataAsString()def json = new JsonSlurper().parseText(response)
// Extract top-level or nested valuesString jwtToken = json.auth.access_tokenint totalOrders = json.data.orders.size()
// Find items matching conditiondef activeOrder = json.data.orders.find { it.status == "ACTIVE" }if (activeOrder) { vars.put("activeOrderId", activeOrder.id.toString())}
vars.put("jwtToken", jwtToken)vars.put("totalOrders", totalOrders.toString())import groovy.json.JsonOutput
def payload = [ timestamp: System.currentTimeMillis(), customerId: vars.get("customerId") ?: "cust-1", actions: [ [action: "PAGE_VIEW", target: "/checkout"], [action: "CLICK_BUTTON", target: "btn_place_order"] ], metadata: [ clientVersion: "v2.4.0", platform: "web" ]]
String jsonString = JsonOutput.toJson(payload)vars.put("dynamicPayload", jsonString)Used for signing requests to AWS, Stripe, or custom cryptographic APIs:
import javax.crypto.Macimport javax.crypto.spec.SecretKeySpec
String secret = "mySuperSecretKey123"String dataToSign = vars.get("timestamp") + "." + vars.get("userId")
Mac mac = Mac.getInstance("HmacSHA256")SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256")mac.init(secretKey)
byte[] rawHmac = mac.doFinal(dataToSign.getBytes("UTF-8"))String signature = rawHmac.encodeHex().toString()
vars.put("apiSignature", signature)import java.time.Instantimport java.time.ZoneIdimport java.time.format.DateTimeFormatterimport java.time.temporal.ChronoUnit
// ISO-8601 UTC timestampString isoNow = Instant.now().toString()
// Future expiration timestamp (7 days from now)String expiresAt = Instant.now().plus(7, ChronoUnit.DAYS).toString()
// Custom formatted dateDateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") .withZone(ZoneId.of("UTC"))String formattedDate = formatter.format(Instant.now())
vars.put("isoNow", isoNow)vars.put("expiresAt", expiresAt)vars.put("formattedDate", formattedDate)if (!prev.isSuccessful()) { String logLine = "${new Date().format('yyyy-MM-dd HH:mm:ss')} | " + "Thread: ${ctx.getThreadNum()} | " + "URL: ${prev.getUrlAsString()} | " + "Code: ${prev.getResponseCode()} | " + "Body: ${prev.getResponseDataAsString()}\n"
new File("jmeter-failures.log") << logLine}vars.get("token"), not "\${token}".log.debug("Processing user {}", userId) instead of string concatenation log.debug("Processing user " + userId) to save memory when debug logging is disabled.if (!prev.isSuccessful()).props.