Checkpoint Lab — Plugins, Custom Components, JMeter APIs, and Extension Strategy
The checkpoint treats a JMeter extension as a small software product. The expected result is not merely “the sampler appears in a menu.” You must prove provenance/hash, Java/JMeter compatibility, unit tests, JMX dependency resolution, bounded runtime behavior, generator/target validity and a rollback path.
Learning objectives
- Complete the chapter checkpoint for Plugins, Custom Components, JMeter APIs, and Extension Strategy as one reviewable, bounded experiment.
- State the workload, predictions, acceptance criteria, authorization boundary, and abort conditions before execution.
- Reconcile configured versus achieved work with JTL, jmeter.log, target evidence, and generator validity before making a conclusion.
- Produce an evidence packet that records the exact inputs, results, diagnosis or gate outcome, and any material limitations.
- Perform cleanup or rollback and explain how the checkpoint evidence hands off to the next chapter or operating practice.
1. Exact assumptions and ceilings
| Item | Checkpoint baseline |
|---|---|
| JMeter/Java | Apache JMeter5.6.3 / Java17. |
| Third-party plugins | None required/installed by the mandatory path. |
| Custom extension | p32-safeid-sampler 1.0.0 built from shown source. |
| API boundary | AbstractJavaSamplerClient + JavaSamplerContext + SampleResult. |
| Activation |
Project-local extensions/ through
search_paths.
|
| Target | 127.0.0.1:8032 only. |
| Workload | 2 threads ×5 loops; 10 Java samples +10 HTTP samples =20 JTL rows. |
| Target ceiling | 10 HTTP requests. |
| Pacing | 150 ms on target HTTP request. |
| Evidence | inventory, JAR/source hashes, compatibility report, unit test output, JMX dependency map, JTL/jmeter.log, target events/stats, rollback log. |
2. Predictions before modification
P1: core-only JMeter startup/smoke works before and after the custom extension because the base installation is not modified.
P2: the extension-dependent JMX run without
search_paths cannot load
devops.academy.p32.SafeIdSampler; this failure proves
the JMX has a real external/custom dependency.
P3: after building/locking the JAR and activating its exact extension path, the plan produces20 JTL samples and10 target requests.
P4: removing the extension path restores core-only operation without deleting/changing shared JMeter JARs.
3. Freeze the pre-change inventory
& "$env:JMETER_HOME\bin\jmeter.bat" -v
java -version
python .\tools\inventory_extensions.py `
--jmeter-home $env:JMETER_HOME `
--project-extensions .\extensions `
--out .\results\inventory-before.json
Review every non-Apache JAR already present. If the base JMeter home is already polluted/unknown, use a clean JMeter5.6.3 distribution before continuing.
4. Compile and unit-test the extension
Use the exact source/commands from Lesson2. Require:
javac --release17core/test PASS;-
adapter compiles against
ApacheJMeter_core.jar+ApacheJMeter_java.jarfrom this JMeter5.6.3 home; -
versioned JAR
p32-safeid-sampler-1.0.0.jarcreated in projectextensions/.
5. Create and verify the compatibility lock
Run lock_extension.py then
check_compatibility.py. Require exact JMeter5.6.3,
Java17 and JAR SHA match. Retain compatibility JSON beside the test
results.
6. Build the JMX dependency map
Save extension-demo.jmx using the Lesson2 tree and run
map_jmx_dependencies.py. Require:
external_or_custom_classes:
- devops.academy.p32.SafeIdSampler
locked_dependencies:
- id: p32-safeid-sampler
version: 1.0.0
class: devops.academy.p32.SafeIdSampler
unresolved_classes: []
status: PASS
The actual JSON contains the generated JAR hash from the lock.
7. Target authorization and preflight
Start extension_target.py on
127.0.0.1:8032 with max10 and verify
/health. Do not allow a target property outside
loopback in this checkpoint. This is inherited directly from
Chapter31's safe-load boundary.
8. Negative compatibility proof
Run the extension-dependent JMX once without the
project search_paths. Preserve the failed
JTL/jmeter.log. The expected problem is custom-class
availability, not target behavior. The fixture should still show
zero/near-zero HTTP target requests because the tree stops on the
extension sampler error.
Do not “repair” this negative run by copying the JAR into shared
lib/ext; the next step activates the exact locked
project path.
9. Authorized compatibility run
$ExtPath = (Resolve-Path .\extensions).Path
& "$env:JMETER_HOME\bin\jmeter.bat" `
"-Jsearch_paths=$ExtPath" `
-n `
-t .\plans\extension-demo.jmx `
-q .\config\extension.properties `
-l .\results\positive\results.jtl `
-j .\results\positive\jmeter.log
Expected independent observations:
-
20 JTL data samples: 10
SafeIdSampler+10LocalTarget; -
target
/stats: requests10/successes10/rejections0; - no ClassNotFound/NoSuchMethodError in jmeter.log;
- generator remains unsaturated; otherwise extension-overhead conclusions are invalid.
10. Evidence packet
| Artifact | Required |
|---|---|
| Core/plugin/custom inventory | Before/after JSON with classification/path/hash/version manifest fields. |
| JAR/source lock | extensions.lock.json with JMeter5.6.3/Java17/JAR SHA/source SHAs/public APIs. |
| Dependency source | Chapter-local source for mandatory custom extension; no third-party plugin. |
| Unit tests | SafeIdCoreTest PASS output. |
| Compatibility matrix | JMeter/Java/hash PASS report. |
| Startup logs | Core-only clean log, negative missing-extension log, positive extension log. |
| JMX dependency map | Custom class resolved to exact locked JAR; no unresolved classes. |
| Runtime evidence | 20 JTL rows + target10 events/stats + generator health. |
| Rollback plan | No shared lib/ext mutation; remove search_paths/use previous locked extension directory. |
11. Upgrade procedure
For a future 1.0.1 candidate:
- create a separate source/build directory and versioned JAR;
- review source/dependencies/API/deprecation warnings and maintainer notes if third-party;
- run pure unit tests;
- generate a candidate lock with new hash—do not overwrite the approved lock yet;
- verify JMeter5.6.3/Java17 compatibility in a clean/local environment;
- run core-only smoke, negative dependency check if useful, then the same 2×5 target-bounded plan;
- compare configured/achieved results and generator state;
- promote the candidate lock/JAR together only after PASS.
Never leave 1.0.0 and1.0.1 JARs containing the same class in the same active extension path.
12. Rollback procedure
Keep the prior approved lock/JAR in a separate inactive versioned
directory. To roll back: stop JMeter/engines, point
search_paths back to the prior directory, verify
hashes/JMeter/Java on every engine/image, run core-only smoke and
the bounded compatibility plan, then preserve the rollback report.
Do not hot-swap JARs inside a running JMeter process.
13. Distributed/CI/container extension rule
If this plan later runs remotely, every engine must pass the same JMeter/Java/extension lock; the current remote manual already requires identical JMeter versions and recommends the same Java. In CI/container environments emit the inventory/compatibility report before load and pin the image/artifact containing the JAR. A controller-only PASS does not prove worker compatibility.
14. Cleanup
- Stop the localhost fixture and confirm port8032 closes.
- Keep evidence/lock/source until review.
-
Deactivate the custom extension by omitting/removing the project
search_paths; do not change shared JMeter home. -
Delete disposable
build/products only after the locked JAR/hash evidence is retained. - No production/shared target, credential, RMI engine, plugin repository or system-wide Java/JMeter configuration was modified.
15. Production operating-model addition and Chapter33 bridge
Chapter32 adds an extension governance contract: core/external/custom inventory, exact JMeter/Java/API compatibility, JAR/source provenance and hashes, JMX dependency mapping, isolated classpath/dependency placement, unit/concurrency tests, generator-cost validation, consistent remote/CI/container inventories, staged upgrades and tested rollback are required before extension-dependent performance results are trusted.
Chapter33 moves to Troubleshooting Out-of-Memory, Socket, SSL, DNS, and Distributed Failures. The classpath and generator-state discipline learned here becomes part of that broader troubleshooting sequence: not every JVM/network/distributed failure is the SUT, and extension drift must be ruled out before deeper runtime tuning.
Knowledge check
Why keep old and new extension JARs in separate inactive/active paths?
Two versions containing the same class can create classpath ambiguity; activation should select exactly one locked version.
What should the negative missing-extension run prove?
The JMX genuinely depends on the custom class and failure is captured without copying random JARs or increasing load.
Why must remote engines share the extension lock?
Each engine executes the plan in its own JMeter/Java/classpath runtime; controller compatibility alone is insufficient.
If the positive run has20 JTL rows but target stats show7 requests, is it valid?
No. The extension/local samples may have run, but configured-versus-achieved target work is incomplete; preserve evidence and diagnose.
What Chapter33 failure class can extension drift resemble?
JVM/classloading/startup/distributed failures, which must be separated from OOM/socket/SSL/DNS/SUT causes.
Official references and version notes
- Apache JMeter downloads — current stable JMeter 5.6.3 and Java 8+ requirement.
- JMeter current changes — Java 17+ recommendation for the 5.6.x line.
-
JMeter Getting Started / classpath
—
lib/ext,lib,search_paths,user.classpathand classpath behavior. - JMeter Properties Reference — current classpath properties and result fields.
- AbstractJavaSamplerClient Javadocs — recommended adapter base class and setup/run/teardown lifecycle.
- JavaSamplerClient Javadocs — per-thread instance behavior and lifecycle.
- AbstractFunction Javadocs — function lifecycle/thread-safety notes.
- JMeter Best Practices — JSR223/Groovy and compiled-script caching guidance.
- JMeter Remote Testing — identical JMeter/Java versions across nodes and distributed runtime behavior.
- JMeter Plugins Manager — optional third-party plugin-management tooling maintained outside Apache JMeter.
Version-sensitive statements were rechecked against current
primary/maintainer documentation on 2026-09-05. The mandatory
runtime is Apache JMeter 5.6.3 with Java 17 and
no third-party plugin. JMeter discovers JMeter component/plugin
jars in JMETER_HOME/lib/ext; dependency/utility jars
belong in JMETER_HOME/lib. Additional
component/plugin paths can be provided with
search_paths; utility/dependency paths use
user.classpath or
plugin_dependency_paths. The
CLASSPATH environment variable does not affect normal
JMeter startup because JMeter is launched with
java -jar. Official Javadocs encourage custom Java
samplers to extend AbstractJavaSamplerClient rather
than implement JavaSamplerClient directly. JMeter
creates a JavaSamplerClient instance per user/thread and calls
setup/run/teardown around that thread's lifecycle. Functions
differ: a function occurrence can be executed by multiple threads,
so mutable non-thread-safe function state needs synchronization or
thread-local design. Current JMeter best practices continue to
prefer cached JSR223 Groovy over BeanShell for intensive
scripting. The JMeter Plugins Manager is maintained by
JMeter-Plugins.org, not by the Apache JMeter project; the
mandatory lab intentionally installs none. If a team adopts it or
any managed plugin, record the exact version, source and SHA-256
and review install/update changes before execution.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.