In JMeter's Include Controller, how is the Filename field resolved into a real path?
answer
- Prefix plus field, then a fallback
- One property, JVM wide
- No functions in that field
- The second try drops the prefix
basics
~20 sJMeter builds the path as the includecontroller.prefix property plus the Filename field. If that file is missing and the combined path is not itself absolute, it retries Filename alone against the running plan's own directory. The field takes no variables or functions.
solid answer
~50 s`IncludeController` concatenates the JMeter property `includecontroller.prefix` with the *Filename* field, trims the result, and opens that. The property defaults to empty and ships commented out in `bin/jmeter.properties`, so out of the box the path is just the field. If the first attempt does not exist **and** that combined `prefix + Filename` path is not itself absolute, the controller falls back to opening the field — **without** the prefix — relative to the base directory JMeter records for the running script, which the CLI sets to the directory holding the `.jmx` named by `-t`. The field itself supports **no variables or functions**; the property is the only knob. Because the prefix is read once into a static field, it is a JVM-wide setting: put it in `user.properties`, or pass it on the command line with `-J`.
code
properties · 3 lines# user.properties on the CI injector
# every Include Controller in every plan gets this prefix
includecontroller.prefix=/opt/jmeter/fragments/go deeper
Recall that the Filename field names a .jmx to pull in and that it takes a plain path only. Typing a variable there does not work, and nothing in the plan substitutes it.
Explain both lookups: the includecontroller.prefix property joined to the field first, then the bare field relative to the directory of the plan the CLI was given, with the prefix dropped.
Show that you use the prefix as the environment seam — the same three plans in version control, a different fragment directory per injector — and that a failed load is logged while the run carries on.
Own where that seam sits for a team: one JVM-wide prefix means a single shared fragment root per machine, so the directory layout has to be decided once rather than per plan.
## The two attempts, in order Apache JMeter 6.0.0's `IncludeController.loadIncludedElements()` does exactly two things with the *Filename* field: 1. It builds `PREFIX + includePath`, trims it, and tries to open that file. `PREFIX` is `JMeterUtils.getPropDefault("includecontroller.prefix", "")`. If the result is a relative path, it resolves against the JVM's working directory — the directory you launched `jmeter` from. 2. If that file does not exist **and** the path it just built (`prefix + field`) is not absolute, it builds a second candidate from `FileServer.getFileServer().getBaseDir()` plus the field. Note that the prefix is **dropped** on this second attempt — and note the corollary: set an *absolute* prefix and the fallback never runs at all, because `prefix + field` is then absolute; a missing file surfaces as a `FileNotFoundException` logged as `File "..." not found for Include Controller "..."`. That base directory is not the working directory. `runNonGui()` calls `FileServer.setBaseForScript(f)` on the plan named by `-t`, and `setBaseForScript` sets the base to that file's absolute parent directory. So the fallback means *"look for this name next to the plan that is running"* — what the manual calls the JMX launch directory. If the fallback runs and neither candidate can be read, the controller logs `Include Controller '<name>' can't load '<file>' - see log for details`, reports the error, and returns nothing. ## What the field will not do The component reference states it in a note: **this element does not support variables or functions in the filename field.** That is not a soft limitation you can work around with cleverness — a `${...}` typed there is not substituted, and an unresolved reference in JMeter does not collapse to an empty string, it renders as the literal text `${...}`. The controller therefore tries to open a path that has a dollar sign and braces in it, and fails to find it. The reason is timing. Includes are resolved while JMeter converts the test tree, before the first thread exists, so there are no thread variables to substitute even in principle. ## The prefix property `includecontroller.prefix` is documented in the properties reference as "Prefix used by `IncludeController` when building file names", defaulting to an empty value, and it ships in `bin/jmeter.properties` commented out: ``` # Prefix used by IncludeController when building file name #includecontroller.prefix= ``` Two properties of the property itself are worth holding on to: - **It is read once, into a `private static final` field.** The lookup happens when the `IncludeController` class initialises, so it behaves as a JVM-wide constant for the run. You cannot vary it per element, and nothing in the plan can change it mid-run. - **It applies to every Include Controller in the plan.** There is no per-controller override. If two fragments live in different directories, the prefix has to be the common ancestor and the field has to carry the rest. That makes it exactly the right shape for an environment knob: the field holds `login.jmx` in version control, and the machine supplies where fragments live. ## A login flow shared by three plans With `login.jmx` holding the Test Fragment, each of the three plans carries an Include Controller whose *Filename* is `login.jmx`, and the injector supplies the directory: - **developer laptop** — no prefix set; the fallback finds `login.jmx` next to the plan being run. - **CI injector** — `includecontroller.prefix=/opt/jmeter/fragments/` in `user.properties`, or passed on the command line, so the same three plans read the deployed copy — and because that prefix is absolute, the plan-directory fallback is switched off on this machine: the deployed copy must be there. One more caveat from the manual applies as soon as you include the same file twice in one plan: give each Include Controller a **different Name**, to avoid the behaviour recorded as Bugzilla 50898. ## Checklist when an include does not load - Does the field contain a `${...}` reference? It will not be substituted. - Is the prefix set on this machine, and does `prefix + field` name a real file from the launch directory? - If you are relying on the fallback, is the file really next to the `.jmx` you passed to `-t`, rather than next to the plan that contains the Include Controller? - Is the failure in the log at all? A file that cannot be loaded is reported and the run **continues** with nothing included, so the plan does not stop to tell you.
- Why can you not put ${envDir} in a JMeter Include Controller's Filename field?The component reference states the element supports no variables or functions there, and includes are resolved while the test tree is built, before any thread or its variables exist. An unresolved reference is not blanked either — JMeter renders it as the literal ${envDir} text, so the controller tries to open a path containing braces and fails.
- Two JMeter Include Controllers in one plan pull in the same login.jmx. What must differ between them?Their Names. The manual warns that when you include the same JMX file more than once you should name each Include Controller differently, to avoid the known issue recorded as Bugzilla 50898. The Filename field can be identical; it is the element name that has to be distinct.
saying these in an interview costs you the question
- Thinks ${dir} in the Filename field will be substituted
- Believes a relative Filename only resolves against the working directory
- Says includecontroller.prefix is configured per Include Controller
- Assumes the fallback attempt also applies the prefix
- Expects a file that cannot be loaded to stop the run