skip to content

Embedded Scripts

Code typed into a test element and run by a scripting engine inside the thread's own path: which element family to pick, what objects it hands you, and what interpreting it costs per sample.

on this pageshow

explore

questions

17

Which JMeter element types come in a JSR223 variant?

level: juniorimportance: must knowfreq 70%

answer

  1. Same base class, different plan slots
  2. Count the slots a script can fill
  3. One is a sampler, one a timer
  4. Six variants in JMeter 6.0.0

basics

~20 s

Apache JMeter 6.0.0 ships six: JSR223 Sampler, JSR223 PreProcessor, JSR223 PostProcessor, JSR223 Assertion, JSR223 Timer and JSR223 Listener. All six extend one base class, so they share the same configuration fields and one process-wide compiled-script cache; each invocation still gets a fresh ScriptEngine from JMeter's single ScriptEngineManager.

solid answer

~40 s

JMeter exposes one JSR-223 scripting engine through six separate elements, each occupying a different slot in a test plan: **JSR223 Sampler**, **JSR223 PreProcessor**, **JSR223 PostProcessor**, **JSR223 Assertion**, **JSR223 Timer** and **JSR223 Listener**. All six extend the same `JSR223TestElement` base class, so the GUI is identical: `Language`, `Parameters`, a `Script File` field whose group is labelled *Script file (overrides script)*, the `Cache compiled script if available` checkbox, and the inline `Script` area. Leave `Language` blank and you get Groovy, the bundled default engine. What differs between the six is the slot each fills and what JMeter does with the value the script returns — the Timer parses it as a delay in milliseconds and the Sampler turns it into response data, while the PreProcessor, PostProcessor and Listener discard it.

go deeper

for a junior

Be able to name the JSR223 variants and say that the Language field falls back to Groovy. Knowing that a sampler produces its own result row while a pre-processor does not is enough here.

for a middle

Explain that all six extend one base class and share the same fields, and that only the Sampler and the Timer do anything with the value a script returns.

for a senior

Show that you choose the variant by the slot you need, and know where a script error becomes visible in the results and where it only reaches jmeter.log.

for a principal

Own the convention: which variants a team may use at all, what is allowed to live in a script, and how a script failure is made visible instead of being left in a log file nobody reads.

## One engine, six sockets Apache JMeter 6.0.0 bundles Groovy as its default JSR-223 engine and offers it through six test elements. Every one of them extends `org.apache.jmeter.util.JSR223TestElement`, which is why the configuration surface is identical in all six: - **Language** — the short name of a `ScriptEngine` on the classpath. The drop-down is built at startup from the engine factories JMeter can see, so it is not a fixed list; the manual notes that a language can be usable without appearing there if its jar is dropped into `lib/`. - **Parameters** — free text handed to the script. - **Script File** — a path. The GUI group is labelled *Script file (overrides script)* and that is literal: a non-empty path makes JMeter ignore the inline `Script` area entirely. A relative path resolves against the `user.dir` system property. - **Cache compiled script if available** — the compilation-cache switch. - **Script** — the inline source. Leave **Language** blank and the base class falls back to the string `groovy`. Name an engine JMeter cannot resolve and the element throws `Cannot find engine named: '<name>'`. ## What each variant is for | Element | Slot it fills | What JMeter does with the return value | |---|---|---| | JSR223 Sampler | a sampler, producing its own `SampleResult` | becomes the response data, unless the script already set one | | JSR223 PreProcessor | a pre-processor | discarded | | JSR223 PostProcessor | a post-processor | discarded | | JSR223 Assertion | an assertion, handed an `AssertionResult` to mark | discarded — you mark the `AssertionResult` instead | | JSR223 Timer | a timer | parsed with `Long.parseLong` and used as a delay in milliseconds | | JSR223 Listener | a sample listener | discarded | The Timer shows most sharply why the variants are not interchangeable. `JSR223Timer.delay()` calls `Long.parseLong` on the returned object's `toString()`; a `null` return logs *Script did not return a value* and yields `0`, and a non-numeric return raises a `NumberFormatException` that is logged and also yields `0`. The Sampler is the only variant that manufactures a result of its own. It labels the result with the element name, records the script text (or `File: <path>`) as the sampler data, and copies the script's return value into the response data if the script has not already set one. ## Where a script error surfaces The six do not agree on this, and the difference matters when you are reading a run afterwards: 1. **JSR223 Sampler** — a `ScriptException` or `IOException` is caught, the result is marked unsuccessful, the response code is set to `500` and the response message is the exception's text. 2. **JSR223 Assertion** — the `AssertionResult` is flagged as an error and carries the exception text as its failure message. 3. **JSR223 PreProcessor, PostProcessor and Listener** — the exception is only logged, as `Problem in JSR223 element named: '<name>'`, and the thread carries on. 4. **JSR223 Timer** — problems are logged and the delay collapses to zero. So a broken script inside a Sampler is loud, and the same broken script inside a PreProcessor is quiet. ## What one looks like in the plan file All six serialise to the same five properties; only the element name changes: ```xml <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Sign request" enabled="true"> <stringProp name="cacheKey">true</stringProp> <stringProp name="filename"></stringProp> <stringProp name="parameters"></stringProp> <stringProp name="script">vars.put("nonce", UUID.randomUUID().toString())</stringProp> <stringProp name="scriptLanguage">groovy</stringProp> </JSR223PreProcessor> ``` ## Picking the right one Reach for the variant whose slot you actually need rather than defaulting to the Sampler. A signature that must exist before a request goes out belongs in a JSR223 PreProcessor attached to that request; a value that must be pulled out of a reply belongs in a JSR223 PostProcessor; a computed pause belongs in a JSR223 Timer, which is the only variant whose return value becomes a delay. Using a JSR223 Sampler for work that is not itself a request adds a result row you then have to explain, unless the script calls `SampleResult.setIgnore()` to keep it out of listeners and later processing.

  • In JMeter, what happens if a JSR223 element has both a Script File path and inline Script text?
    The file wins. `processFileOrScript` tests the file name first, and the GUI group is labelled *Script file (overrides script)*. The inline text is never evaluated. If the path is not an existing readable file the element throws a `ScriptException` naming the absolute path rather than quietly falling back to the inline script.
  • What does a JMeter JSR223 Timer do when its script returns something that is not a number?
    `JSR223Timer.delay()` calls `Long.parseLong` on the returned object's `toString()`. A `null` return logs *Script did not return a value* and produces a zero delay; a non-numeric value raises a `NumberFormatException` that is logged and also produces zero. Either way the thread does not pause and the run continues.

saying these in an interview costs you the question

  • Says JSR223 exists only as a sampler
  • Thinks each variant has its own scripting subsystem or its own set of configuration fields
  • Expects a PreProcessor's return value to be used
  • Assumes Script File is only a fallback for the inline script
  • Believes the Language field must always be filled in
open as a page

In a JMeter JSR223 PostProcessor, which bound object hands a captured value to the next sampler?

level: juniorimportance: must knowfreq 72%

basics

~10 s

The vars binding, which is the running thread's JMeterVariables object. Call vars.put("orderId", id) inside the script and any later element in that same thread reads the value as ${orderId}.

open as a page

In JMeter, why does a BeanShell element re-pay its script cost on every call?

level: middleimportance: must knowfreq 62%

basics

~20 s

JMeter caches nothing for a BeanShell element. Every invocation hands the script straight to that element's bsh.Interpreter, calling eval() on the inline Script field or source() on a Script file, so the interpretation cost repeats per sample.

open as a page

What does a JMeter JSR223 element's 'Cache compiled script if available' checkbox actually do?

level: middleimportance: must knowfreq 64%

basics

~20 s

It lets JMeter compile the inline Script once and reuse the compiled form instead of handing the source to the engine on every execution. It only bites if the engine implements javax.script.Compilable, as Groovy does.

open as a page

What does JMeter's 'Reset bsh.Interpreter before each call' option cost you?

level: middleimportance: should knowfreq 38%

basics

~20 s

Ticking it throws away the element's interpreter and builds a new one on every invocation, re-sourcing the init file if one is configured. You pay construction per call, and the script loses any state it kept between calls.

open as a page

Why does ${signingKey} inside a JMeter JSR223 element's Script File reach Groovy unresolved?

level: middleimportance: should knowfreq 55%

basics

~10 s

JMeter substitutes variable and function references only in the inline Script field. A Script File is read straight off disk and handed to the engine byte for byte, so ${signingKey} arrives as literal text.

open as a page

In a JMeter JSR223 PreProcessor, which sample result does the bound prev object hold?

level: middleimportance: should knowfreq 45%

basics

~20 s

The result of the previous sampler, not the one about to run. JMeter only calls setPreviousResult after a sample completes, so in a PreProcessor prev is one sampler behind, and it is null before the thread's first sample.

open as a page

In a JMeter JSR223 script, how do vars.put and vars.putObject differ?

level: middleimportance: should knowfreq 52%

basics

~20 s

vars.put is declared put(String, String) and stores a String. vars.putObject takes any Object. Both write into the same per-thread map, and vars.getObject is the only accessor that gives the stored object back with its type.

open as a page

In JMeter, why can one ${__BeanShell(...)} call serialise every thread in the plan?

level: seniorimportance: should knowfreq 26%

basics

~20 s

Each occurrence of a function call is one Function instance shared by all threads, and JMeter's BeanShell function declares execute() synchronized. Every thread that reaches that field therefore queues on one monitor and one interpreter.

open as a page

A JMeter JSR223 PreProcessor signs each request from an inline script, yet every request after the first carries the same signature. Why?

level: seniorimportance: should knowfreq 42%

basics

~10 s

The inline script contains ${...} references and compilation caching is on. JMeter compiles the first substituted version of the text and keeps reusing it, so every later execution re-runs the first request's values.

open as a page

Your JMeter JSR223 script calls log.debug and nothing reaches jmeter.log. Why?

level: seniorimportance: should knowfreq 40%

basics

~20 s

The shipped bin/log4j2.xml sets the root logger to info, so debug events are dropped. The log binding is a real SLF4J Logger named after the element's class plus its tree name; raise that name's level, or use log.info.

open as a page

You inherit a JMeter plan whose BeanShell elements cap the injector. How do you plan the move off them?

level: principalimportance: should knowfreq 41%

basics

~20 s

Sort the scripts before rewriting any. Delete the dead ones, replace the ones duplicating a stock element, and convert only what genuinely encodes behaviour, ranked by how often it runs rather than by how long it looks.

open as a page

Would you standardise a team's JMeter JSR223 elements on inline Script text or on Script Files?

level: principalimportance: should knowfreq 38%

basics

~20 s

There is no single right answer. Script Files are reviewable and always compiled; inline text keeps a plan self-contained and is the only form JMeter substitutes variables into. Pick one rule and write it down.

open as a page

Your team's JMeter scripts reach through ctx into the thread and engine objects. Where do you draw the line?

level: principalimportance: should knowfreq 28%

basics

~20 s

Tier the surface. JMeterContext's read-only accessors are fair game; the thread, thread group and engine handles, and every method the source marks as called internally by JMeter, should need a review before a plan depends on them.

open as a page

In JMeter, how do you catch a Groovy syntax error in a JSR223 element without running the plan?

level: middleimportance: nice to knowfreq 28%

basics

~20 s

Use the GUI's Tools menu entry 'Compile JSR223 Test Elements'. It walks the whole plan, compiles every enabled JSR223 element whose engine supports compilation, marks the ones that fail in the tree and reports how many broke.

open as a page

In JMeter, what changes in a run when you set beanshell.sampler.init?

level: seniorimportance: nice to knowfreq 16%

basics

~20 s

The named file is sourced into every BeanShell Sampler interpreter as it is built, so once per element per thread. Setting it also switches on the element's threadStarted, threadFinished, testStarted and testEnded script callbacks, which are otherwise never dispatched.

open as a page

Which objects does JMeter bind for a script named by the jsr223.init.file property?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

Only three: log, props and OUT. JMeter builds that script's bindings by hand in its own startup path and never touches a JMeterContext, so there is no vars, ctx, prev, sampler, Label, FileName, Parameters or args at all.

open as a page