Why can a value set in JMeter's system.properties be invisible to ${__P(...)}?
answer
- Loaded last is not the same as wins
- Two files share a map, one does not
- Think defaults rather than entries
- Check whether jmeter.properties already defines the key
basics
~10 sBecause system.properties updates the JVM's system properties, which JMeter consults only as a fallback. Any key already present in jmeter.properties or user.properties shadows it, so the last file loaded still loses.
solid answer
~40 s`JMeterUtils.loadJMeterProperties` builds JMeter's store as `new Properties(System.getProperties())`, which makes the JVM's system properties the map's **defaults** rather than its entries. `jmeter.properties` and `user.properties` are then loaded as real entries in that map, while the file named by `system.properties` is loaded into `System.getProperties()`. `java.util.Properties.getProperty` checks its own entries before its defaults, so `${__P(key)}` finds a `jmeter.properties` or `user.properties` value first and falls back to a system property only when the key is missing from both. Hence the surprise: `system.properties` is loaded **last**, yet for any key the shipped `jmeter.properties` already defines it never wins. Load order and precedence are different things here. Use `system.properties` for values the JVM or a library reads, and `user.properties` for values your plan reads through `${__P(...)}`.
go deeper
Know that JMeter has more than one property file and that they are not interchangeable. If an override seems ignored, the file it was written in is a reasonable first suspect.
Explain that system.properties updates the JVM's system properties rather than JMeter's own map, and that the two are separate stores even though one function reads across both.
Reason about the chain out loud. JMeter's map holds system properties as defaults, own entries are checked first, so a key defined in the shipped jmeter.properties shadows the same key set in system.properties.
Set the rule for the team so this never costs an afternoon twice. Plan parameters go in one file, JVM and library settings in another, and the split is written down rather than inferred from a failing run.
This is the case where load order and precedence pull in opposite directions, and reading only the documented startup sequence leads you to the wrong prediction. The startup list is accurate — `system.properties` really is loaded after `jmeter.properties` and `user.properties` — but "loaded last" does not mean "wins", because the three files do not all load into the same map. ## Two maps, chained `JMeterUtils.loadJMeterProperties` builds JMeter's property store like this: Properties p = new Properties(System.getProperties()); That single-argument constructor does not copy anything. It creates an empty `Properties` whose **defaults** are the JVM's system properties. The file's contents are then loaded as the map's own entries, and the result is assigned to the static field JMeter uses for the rest of the run. `java.util.Properties.getProperty` looks in its own entries first, and consults `defaults` only when the key is absent. So JMeter's property lookup has two tiers: 1. Own entries — everything loaded from `jmeter.properties`, then everything merged in from `user.properties`. 2. Fallback — the JVM's system properties, which is where the `system.properties` file, and the JVM's own startup values, end up. ## Why the last file loaded can still lose `${__P(name,default)}` resolves through `JMeterUtils.getPropDefault`, which calls `getProperty` on that chained map. Put the two facts together: - A key present in `jmeter.properties` or `user.properties` is an **own entry**, so it is found in tier one and the fallback is never consulted. - A key set only through the `system.properties` file lives in the **defaults**, so it is found only when tier one has nothing for that key. The result is that `system.properties` behaves like a **low-priority backstop** for JMeter property lookups, in spite of being loaded last. The stock `jmeter.properties` is a long file, but almost all of it is commented-out documentation of defaults — it actually sets only about three dozen keys. Those live entries, and anything you add to `user.properties`, shadow the same name set in `system.properties` before your plan ever runs; the several hundred keys that appear only as `#key=value` comments define nothing and shadow nothing. ## The precedence you can rely on For a `${__P(key)}` lookup, from strongest to weakest: | Rank | Source | Why | |---|---|---| | 1 | the file named by `user.properties` | merged into own entries last | | 2 | `bin/jmeter.properties` | own entries, established first | | 3 | the JVM's system properties, including the `system.properties` file | reached only as the map's defaults | Note that ranks 1 and 2 are settled by merge order within one map, while rank 3 is settled by the defaults chain and cannot be changed by loading earlier or later. ## Which file to use for what - Values your **plan** reads with `${__P(...)}` belong in `user.properties`. That is the only file that reliably overrides the shipped defaults. - Values the **JVM or a library** reads — TLS settings, proxy configuration, anything looked up through `System.getProperty` by code you do not control — belong in `system.properties`. Those consumers read the system properties directly, so the defaults-chain demotion never applies to them. ## Recognising the symptom The tell is a value that appears to be ignored even though the file is definitely being read and the spelling is definitely right. Before suspecting the file, check whether the same key already exists in `jmeter.properties`. If it does, moving the entry to `user.properties` fixes it immediately, and that one move is usually faster than proving where the value came from. The general lesson is worth carrying: in JMeter 6.0.0 the order in which property files load tells you how the map is assembled, not which tier a lookup will find first.
- If system.properties is demoted like that, what is it actually for?Values read straight from the JVM rather than through JMeter, such as TLS, proxy or DNS settings that a library looks up with System.getProperty. Those consumers never go through JMeter's map, so the defaults-chain demotion does not affect them at all.
- Which file should hold an override that a plan reads with ${__P(...)}?The file named by the user.properties key. Its entries are merged into JMeter's own map after jmeter.properties, so they beat the shipped defaults and are never demoted to the fallback tier the way a system property is.
saying these in an interview costs you the question
- Assumes the last property file loaded always wins
- Says system.properties and user.properties feed the same map
- Puts plan parameters in system.properties and expects them to override
- Thinks ${__P(...)} cannot reach a system property at all
- Blames a typo when the key is really being shadowed