Kept for this browser session. No learning progress is saved.
Print this complete view or save it as PDF before going offline. The Markdown worksheet includes both command platforms.
Java foundations · Local Java 21 lab
Messaging with Apache Artemis
An online shop accepts orders while billing and shipping work independently. Follow an order through queues, events and a controlled failure, using evidence from your own local broker.
3 stations3 hours 45 minutesContent v1.0.0
Prepare your environment
Java classes, loops, exceptions and basic Maven usage. No messaging experience required.
A supplied JDK 21 and four separate cmd.exe terminals on Windows, or shells on Linux/macOS.
An invitation to the private training repository for editable source, or the trainer’s verified runtime ZIP for demonstrations.
Your own local broker. Docker, administrator rights and a remote broker are not required.
The trainer can distribute a verified runtime ZIP and JDK 21 before the session. Extract both and follow RUN.md; local demonstrations need no Maven or network. Editing exercises needs the full source and a build environment. An offline source build additionally requires a prepared Maven Wrapper distribution and dependency/plugin cache. A runtime ZIP alone cannot compile exercises.
Repository access does not follow from visiting these public pages. Invited collaborators can see the sample solutions and other branches. On GitHub, download an artifact from a successful Actions run for the agreed revision, extract its runtime ZIP, and read RUN.md. Private artifacts expire after seven days; your trainer should prepare them before the session.
Work on your own local broker first. With a partner, alternate driver and navigator every 15–20 minutes: one operates, the other predicts and reconciles IDs. Each person demonstrates one changed condition.
Make your initial prediction before revealing hints or asking AI. AI assistance is optional and subject to company policy. Verify suggestions against your code and logs; do not provide credentials, confidential code or company data. You never need to upload prompts or chat histories.
Use the worksheet or local notes for predictions and evidence. These pages do not execute commands or collect your answers. The checklist is your own self-assessment.
Set up once, in separate terminals
Windows commands are selected initially. Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP). Check java -version and javac -version; both should use your supplied JDK 21. Set JAVA_HOME to its root directory if needed. Configure company proxies/certificates through approved IT settings.
Setup terminal
Get source and build once
Parent directory for your training checkout
Accept your personal GitHub invitation first. Replace YOUR_GITHUB_NAME with your own username in the branch name. The branch starts at the verified English revision below, even while main changes. These commands need Git and initial download access. After cd, run subsequent steps from this repository root.
Observe: The training profile builds editable exercises and separate reference solutions. Its tests verify the reference implementation; green CI does not grade every trainee solution.
Terminal 1 — broker
Start the broker in the foreground
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Keep this terminal open. Do not paste client commands into it. Wait for BROKER_READY before continuing in another terminal.
.\scripts\broker.cmd start
Observe: BROKER_READY. Default TCP address: 127.0.0.1:61616, local loopback only.
Terminal 3 — inspection
Check the runtime connection
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Open a separate terminal in the same lab root. All station commands below use explicit namespaces. For a repeat, replace the namespace in every cooperating client with a fresh value.
.\scripts\doctor.cmd runtime
Observe: TCP and authentication successful. The broker remains running in Terminal 1.
Choose the right entry point:lab runs finished demonstrations; exercises runs your editable implementations; solutions runs separate worked examples. Build source changes again with the training profile. Do not mix their messages in an old namespace.
No Docker, admin rights, PowerShell policy change or external broker is required. The optional central-broker profile is follow-up material and needs a separate company-network preflight.
Station 1 of 3 · 45 minutes · S01 / S02 · Exercise A
Queues and competing consumers
Explain how a queue retains waiting work and how consumers share it.
Shipping workers process order commands. When workers stop, orders should wait. When a second worker joins, it should help with the same backlog.
Order producer→One queue→Worker Alice / Worker Bob
The producer sends to one queue. Alice and Bob compete for its work; they do not each receive a full copy.
Prerequisites: Java foundations and the common setup, with Terminal 1 showing BROKER_READY. Use your own local broker. Keep a fresh namespace consistent across cooperating clients.
1. Predict
If two workers consume the same queue, which orders will each worker receive? What remains available when both workers stop?
Write your prediction in the worksheet before commands, hints or AI. Keep the sent and received business-ID sets as evidence.
My prediction: ____________________________________________________
2. Run
Terminal 3 — producer / inspection
Send with no worker
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Leave Terminal 2 idle. Terminal 1 must already show BROKER_READY. Use pilot-q1 only for a fresh run.
Observe: Ten SEND business IDs and visible=10. Copy the ID set into your worksheet.
Terminal 2 — worker Alice
Process the waiting batch
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Use the same namespace as the producer. This bounded worker exits after ten deliveries.
.\scripts\lab.cmd s01 consume --worker alice --count 10 --namespace pilot-q1
Observe: Ten COMMIT records. Reconcile the committed business IDs with the sent set.
Terminal 3 — inspection
Inspect after the worker exits
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Wait for Terminal 2 to finish before browsing.
.\scripts\lab.cmd s01 browse --namespace pilot-q1
Observe: visible=0. Browsing is a snapshot and excludes active deliveries.
Terminal 2 — worker Alice
Start the first competing worker
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Use a new namespace, pilot-q2. This worker stays active; open Terminal 3 separately.
.\scripts\lab.cmd s02 consume --worker alice --continuous --sleep 150 --namespace pilot-q2
Observe: Wait for READY in Terminal 2.
Terminal 3 — worker Bob
Start the second competing worker
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Do not publish until BOTH workers show READY. Each worker owns a connection and transacted session.
.\scripts\lab.cmd s02 consume --worker bob --continuous --sleep 150 --namespace pilot-q2
Observe: Wait for READY in Terminal 3.
Terminal 4 — producer / inspection
Send shared work
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Only after both READY signals. Keep the workers running until the combined committed set is complete.
.\scripts\lab.cmd s02 produce --count 10 --namespace pilot-q2
Observe: Both workers participate. The union of their committed IDs equals the sent set, and their sets are disjoint in this controlled failure-free run. An equal split or alternating order is not required.
My evidence / ID sets: ____________________________________________
3. Break
Terminals 2 and 3 — workers
Stop both workers after the complete set
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Use Ctrl+C in each worker terminal after all ten business IDs have committed. Do not crash during processing in this experiment; Station 3 covers that case.
Observe: Both worker processes have exited.
Terminal 4 — producer / inspection
Create a backlog
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
First confirm visible=0, then send three orders while both workers remain stopped.
Observe: The last browse shows visible=3. Work is retained even though processing has stopped.
My evidence / ID sets: ____________________________________________
4. Explain
Where did the three waiting orders live after the producer exited?
Show the sent and committed ID sets. Why do ten total commits not imply ten at each worker?
Find receive, business work and session.commit() in the private code. How does a missing commit change the conclusion?
Staged hints
Use one hint at a time after recording your prediction.
Hint 1
A queue retains work for its consumers. Two consumers on that queue share deliveries; they are not independent subscriptions.
Hint 2
Use a transacted session per worker. Receive, perform the simulated work, then commit. Use continuous mode when each worker’s share is unknown and compare the combined business-ID set.
Check your understanding
Choose an answer, then check it for feedback. These questions do not affect quiz exam scoring.
1. A producer sends persistent orders with no worker running. What should you observe?
The orders wait at the broker.
All orders disappear when the producer exits.
A second producer must stay connected.
My answer and reason: ______________________________________
2. Two workers consume one queue. Does each receive a complete copy?
Yes, both must receive all ten orders.
No. They share the work on that queue.
My answer and reason: ______________________________________
3. Which is a valid success criterion for the controlled two-worker run?
Exactly five commits per worker, alternating every time.
The complete combined ID set, no duplicate commits, both workers participating.
My answer and reason: ______________________________________
5. Adapt
Complete or extend AExercise.java in the private exercises module. Implement the producer and transacted consumer using direct Jakarta Messaging calls. Change batch size and processing delay, predict the new result, then compare the ID sets. Use a fresh namespace in every exercise client; A uses S02 destinations. Rebuild with -Ptraining. Worked solutions remain in the separate private solutions module.
Reflect on the change
What did you predict, what changed, and which IDs or state support your explanation?
Which suggestion did you accept or reject, and what evidence informed that decision? If you used no assistance, explain the decision you made from the supplied material.
My adaptation and explanation: ____________________________________
Completion checklist
This is a local self-assessment. Nothing is submitted or saved.
Clean up this station
Terminal 2 — worker
Drain the known three-order backlog
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Both continuous workers must be stopped. Process the three orders created in the Break step.
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Wait for the cleanup worker to exit. Leave the local broker running for Station 2.
.\scripts\lab.cmd s02 browse --namespace pilot-q2
Observe: visible=0. If your adaptation used another namespace, drain its known remaining count separately.
Station 2 of 3 · 45 minutes · S03 · Exercise B · S04 optional
Events and independent subscribers
Distinguish shared work from independent interest in the same event.
An accepted order matters to both billing and shipping. Each service needs its own complete set of future OrderCreated events, even though they work at different speeds.
Order events → topic↳Billing subscription↳Shipping subscription
The topic fans out to independent billing and shipping subscriptions. Both active subscribers receive every published event; they do not share one queue.
Prerequisites: Java foundations and the common setup, with Terminal 1 showing BROKER_READY. Use your own local broker. Keep a fresh namespace consistent across cooperating clients.
1. Predict
How will billing and shipping differ from the two workers in Station 1? Which events will a subscriber created after the first batch receive?
Write your prediction in the worksheet before commands, hints or AI. Keep the sent and received business-ID sets as evidence.
My prediction: ____________________________________________________
2. Run
Terminal 2 — billing
Create the billing interest
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Terminal 1 remains the local broker. Billing and shipping use independent non-durable consumers, each with its own connection. Start the other subscriber and producer within the 60-second receive timeout; if you time out before publishing, restart both in a fresh namespace.
Observe: READY in Terminal 3. Both subscribers must now be ready.
Terminal 4 — producer
Publish the first three events
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Only after both subscribers show READY. Record the SEND IDs and compare them with each subscriber’s COMMIT set after both exit.
.\scripts\lab.cmd s03 produce --count 3 --namespace pilot-events
Observe: Billing receives all three business event IDs. Shipping independently receives those same three IDs. Repeated IDs across independent subscriptions are expected copies.
Terminal 2 — late subscriber
Create a late interest
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
The initial billing process has exited. This new non-durable subscriber is created after the first batch. Its CLI subscription label does not register an offline durable identity.
.\scripts\lab.cmd s03 subscribe --subscription late --worker late --count 2 --timeout 60000 --namespace pilot-events
Observe: READY for late, with no replay of the earlier three events.
Terminal 4 — producer
Publish two future events
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Wait for late to show READY first. Compare its set only with this second SEND set.
.\scripts\lab.cmd s03 produce --count 2 --namespace pilot-events
Observe: Late receives exactly the two future IDs and exits. It does not receive the original three.
My evidence / ID sets: ____________________________________________
3. Break
Terminal 4 — producer
Publish before the next subscriber exists
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
All prior bounded subscribers must have exited. Predict the result before sending. Use a fresh namespace for this controlled missing-history experiment.
.\scripts\lab.cmd s03 produce --count 2 --namespace pilot-late-break
Observe: Two SEND records. There is no active non-durable subscriber to receive them.
Terminal 2 — new subscriber
Observe missing history with a bounded wait
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Now subscribe, without sending any new events. The five-second timeout is deliberate. Do not retry indefinitely or assume a networking failure.
.\scripts\lab.cmd s03 subscribe --subscription after --worker after --count 2 --timeout 5000 --namespace pilot-late-break
Observe: READY, then TIMEOUT received=0 and exit code 2. This subscriber did not exist for the earlier sends, so the result is expected.
My evidence / ID sets: ____________________________________________
4. Explain
Compare the two complete first-batch ID sets with Station 1’s shared set. Where was a copy created?
Why did late receive only the second batch? Why does the CLI label not create durability?
What would need to be registered before an offline period? Keep durable subscription identity and persistent message delivery as separate properties. S04 is an optional follow-up.
Staged hints
Use one hint at a time after recording your prediction.
Hint 1
Independent interests need independent subscriptions. Two consumers on the same queue would divide one copy.
Hint 2
Create each consumer, call connection.start(), then signal READY before publishing. A new non-durable consumer has no prior history. Durable interest must be registered first with a stable identity.
Check your understanding
Choose an answer, then check it for feedback. These questions do not affect quiz exam scoring.
1. Billing and shipping have separate active subscriptions. Three events are published after both are READY. What is expected?
Three events in total shared between the services.
The complete three-event set at each service.
My answer and reason: ______________________________________
2. A new non-durable subscriber starts after the first three events. What history does it receive?
All previous events because it has a subscription label.
None of that earlier batch; only events published while it is active.
My answer and reason: ______________________________________
3. Which change can retain events for an offline interest?
Register a durable subscription before the offline sends and reconnect with its stable identity.
Create a new non-durable subscription after publishing.
My answer and reason: ______________________________________
5. Adapt
Complete or extend BExercise.java in the private exercises module. Create a third independent interest (for example, analytics), each on its own connection, using the same topic. Start all three and confirm READY before publishing. Predict which future IDs each receives. Use a fresh exercise namespace; do not replace independent subscriptions with consumers sharing one queue. Rebuild with -Ptraining.
Reflect on the change
What did you predict, what changed, and which IDs or state support your explanation?
Which suggestion did you accept or reject, and what evidence informed that decision? If you used no assistance, explain the decision you made from the supplied material.
My adaptation and explanation: ____________________________________
Completion checklist
This is a local self-assessment. Nothing is submitted or saved.
Clean up this station
Terminals 2 and 3 — subscribers
Close remaining non-durable consumers
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
The bounded subscribers should have exited. Stop any adaptation consumers still running with Ctrl+C. Keep the local broker in Terminal 1 for Station 3.
Observe: No subscriber process remains active. Non-durable subscription resources disappear with their connections; there is no durable identity to unsubscribe in S03.
Station 3 of 3 · 60 minutes · S05 / S06 · Exercise D
Acknowledgement, retry and dead letters
Explain what is confirmed, what can be delivered again, and how failed work is recovered deliberately.
A shipping worker receives an order and fails before confirming it. Another order contains invalid business data. Recover deliberately without treating a receive as a completed business effect.
A transacted worker commits healthy orders or rolls back failed deliveries. After three failed total attempts, invalid work moves to a dead letter queue for inspection and deliberate repaired replay.
Prerequisites: Java foundations and the common setup, with Terminal 1 showing BROKER_READY. Use your own local broker. Keep a fresh namespace consistent across cooperating clients.
1. Predict
If a worker receives an order and stops before JMS commit, can that same business ID return? Will waiting and retrying repair an invalid order?
Write your prediction in the worksheet before commands, hints or AI. Keep the sent and received business-ID sets as evidence.
My prediction: ____________________________________________________
2. Run
Terminal 3 — producer
Send one identifiable order
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
The local broker remains in Terminal 1. Use a fresh namespace for this rollback experiment.
.\scripts\lab.cmd s05 produce --count 1 --id ack-demo --namespace pilot-ack
Observe: One SEND. Record its business ID and JMSMessageID separately.
Terminal 2 — worker
Receive, then roll back
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
This worker intentionally rolls back one delivery and exits. It does not commit a successful business result.
Observe: Same business ID, JMSRedelivered=true and attempt=2 in this controlled run; then COMMIT. An unavailable delivery count is reported as unknown, not invented.
My evidence / ID sets: ____________________________________________
3. Break
Terminal 3 — producer
Prepare a fresh crash experiment
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Choose a separate namespace so prior delivery attempts do not affect this comparison.
.\scripts\lab.cmd s05 produce --count 1 --id crash-demo --namespace pilot-crash
Observe: One fresh business ID to compare across the crash.
Terminal 2 — worker
Crash before JMS commit
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
The --crash option deliberately terminates this client process. Exit code 17 is expected; the broker remains running.
Observe: Two healthy COMMITs, three ROLLBACKs for the invalid business ID, approximately 500 ms redelivery delay. Three total attempts are two additional retries. Healthy work continues.
Terminal 3 — inspection
Inspect before repairing
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Wait for the worker to exit. DLQ means dead letter queue: failed work retained for investigation after the attempt limit.
Observe: Exactly one failed business ID, matching order-2. Browsing does not consume it.
Terminal 3 — deliberate replay
Repair and replay the known failed order
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
The demonstration repairs priorityClass in both JSON payload and JMS properties, then commits its DLQ receive and new send together. Replaying without fixing invalid data would fail again.
Observe: One successful COMMIT for the repaired business ID.
My evidence / ID sets: ____________________________________________
4. Explain
In a transacted session, commit confirms receives and publishes sends against the same broker; rollback reverses both. Find these calls in the private Java code.
AUTO_ACKNOWLEDGE confirms a synchronous delivery when receive returns successfully, or a listener delivery after onMessage returns successfully. Later business failure is not automatically protected.
CLIENT_ACKNOWLEDGE acknowledges all messages consumed so far in that session, not just the individual message object. These lab workers instead use explicit transacted sessions.
Compare the business ID and delivery metadata before and after rollback/crash. Explain three total attempts versus two retries and why invalid business data needs repair.
At-least-once delivery permits duplicates. A JMS commit does not prove an external payment or email happened once. S09’s atomic local H2 effect is a later extension, not an end-to-end exactly-once guarantee.
Staged hints
Use one hint at a time after recording your prediction.
Hint 1
Locate the transaction boundary. A receive and an external business effect are different events. An uncommitted delivery can be delivered again.
Hint 2
Rollback priorityClass=invalid; commit healthy work. With three produced messages, count=5 includes two healthy deliveries plus three poison attempts. Repair both payload and property on deliberate replay, preserving the business ID.
Check your understanding
Choose an answer, then check it for feedback. These questions do not affect quiz exam scoring.
1. A transacted worker crashes after receive and before JMS commit. What must you allow for?
The same business ID can be delivered again.
Receive proves the external business effect happened exactly once.
My answer and reason: ______________________________________
2. The broker permits three total delivery attempts. How many additional retries follow the first attempt?
Three retries.
Two retries.
My answer and reason: ______________________________________
3. What is the deliberate recovery path for invalid business data in the DLQ?
Replay forever until waiting makes the data valid.
Inspect, repair payload and properties, preserve the business ID, then replay and verify.
My answer and reason: ______________________________________
5. Adapt
In private DExercise.java, implement the invalid-data failure branch: rollback when priorityClass=invalid, otherwise commit after successful work. Identify receive/work/commit in the code, rebuild with -Ptraining, then repeat in a fresh namespace and predict the five-delivery result. If time remains, implement inspection and repaired replay yourself; the full exercise can continue after the pilot. Keep complete worked solutions in the private solutions module.
Reflect on the change
What did you predict, what changed, and which IDs or state support your explanation?
Which suggestion did you accept or reject, and what evidence informed that decision? If you used no assistance, explain the decision you made from the supplied material.
My adaptation and explanation: ____________________________________
Completion checklist
This is a local self-assessment. Nothing is submitted or saved.
Clean up this station
Terminal 3 — inspection
Verify empty input and DLQ
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Wait for the repaired worker to exit. These two snapshots should agree with your recorded commits.
Observe: Both show visible=0. Continue to the lab recap to stop your owned local broker.
Debrief and finish
Show a partner or trainer one changed condition. Explain where work waited, who received a copy, and what was confirmed before failure. Support your explanation with business IDs and queue state.
Stop all workers with Ctrl+C. Ask the local broker to stop and wait for its process to exit. A full reset is optional and deletes your owned broker messages; use it only for an exclusively owned local lab after every client and the broker have stopped. Ownership-marker, path and process-lock guards must remain intact. The H2 ledger is separate. Never reset a shared remote broker for an individual exercise.
Terminal 4 — control
Stop your local broker
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
All workers must already be stopped. After this command, wait for Terminal 1 to exit.
.\scripts\broker.cmd stop
Observe: Graceful stop completes; broker process exits.
Optional full local reset
Terminal 4 — control
Reset only your stopped, owned local broker
Lab root: the folder containing mvnw.cmd (source) or RUN.md (runtime ZIP).
Only if every client and broker process has exited, and you intentionally want to remove this local broker dataset. Do not edit ownership markers to bypass a refusal.
.\scripts\broker.cmd reset --confirm
Observe: Owned broker data deleted; H2 ledger preserved. Use fresh namespaces for later runs.
Optional next steps in the private repository: S04 durable subscriptions; S09 idempotency for one local DB effect; selectors and other scenarios. A first pilot with two trainees will help refine timing and clarity.