Foundation · Prepare
Professional Environment Setup
Install the IDE, JDK, HTTP client, and Docker setup that every later module assumes is working.
Primary source: Spring Boot System Requirements: Java 17 minimum, Java 21 LTS recommended
Lesson 3 of 7
1d streakThe Architectural Question
Version mismatches between your machine, teammates, and CI produce failures that look like bugs in your code but are bytecode and daemon problems.
What this guide proves
- IntelliJ / VSCode
- JDK 21
- Docker Desktop
Know what each tool owns
A reproducible workstation is a set of agreeing versions, not a collection of installed icons. This course deliberately targets Java 21 and the Spring Boot 3.5 maintenance line. Java 21 is a long-term-support release and the course target; it is not the newest LTS. Spring Boot 3.5 requires at least Java 17 and supports newer releases, but using one target across the terminal, IDE, wrapper, and CI removes an entire class of false debugging leads.
| Tool | Responsibility | Proof it is ready |
|---|---|---|
| JDK 21 | Compile and run Java | java and javac report major version 21 |
| IDE/editor | Edit, navigate, debug, and run tests | Project SDK and language level are 21 |
| Maven Wrapper | Resolve dependencies and run the build | ./mvnw --version uses Java 21 |
| Git | Record and compare source changes | git --version succeeds |
| Docker | Run disposable supporting services | docker version reaches the daemon |
| HTTP client | Send and inspect requests | A local fixture returns headers and a body |
| PostgreSQL | Practice relational data and transactions | psql or a container accepts a query |
One canonical path
Install and verify Java 21
Use a trusted JDK distribution from your operating-system package manager or a vendor such as Eclipse Temurin, Microsoft, Oracle, or Amazon Corretto. Follow that vendor’s OS-specific instructions instead of pasting a remote script into a shell. After installation, open a new terminal so PATH changes take effect.
java --version
javac --version
# macOS and Linux
echo "$JAVA_HOME"
# Windows PowerShell
$env:JAVA_HOMEopenjdk 21.x ...\njavac 21.x
java selects the runtime and javac selects the compiler. Both must resolve to a JDK 21 installation. JAVA_HOME should point to the JDK directory, not its bin folder. If the versions disagree, inspect PATH ordering before reinstalling anything. The Maven wrapper prints the JVM it actually uses, which is the decisive build-time check.
Active Recall Checkpoint
Your terminal reports Java 21, but ./mvnw --version reports Java 17. Is setup complete?
Show answer & explanation
No. The build is running under a different JVM. Align JAVA_HOME, shell PATH, Maven toolchains if present, and the IDE project SDK before continuing.
Configure the editor and build boundary
IntelliJ IDEA, VS Code, and other Java-capable editors can all complete the course. IntelliJ IDEA Ultimate includes deeper Spring tooling; Community Edition can still edit, compile, test, debug, and run Maven applications. Treat Spring-specific visual assistance as convenience rather than a prerequisite. The application must remain buildable from a plain terminal because CI does not use your IDE configuration.
git --version
./mvnw --version
./mvnw clean verifyCommit .mvn/wrapper, mvnw, and mvnw.cmd. The wrapper pins the Maven distribution used by contributors and CI, while the POM pins the project model. A globally installed mvn can be useful for administration, but course commands use ./mvnw so the repository remains the source of truth.
Do not hide a failing terminal build with an IDE run configuration
./mvnw clean verify fails, capture both JVM versions and fix the mismatch. A private IDE classpath is not a reproducible build.Verify Docker and PostgreSQL independently
Docker Desktop is common on macOS and Windows; Docker Engine is common on Linux. Install it from the official Docker documentation, start the daemon, and verify that the client can reach it. Then run PostgreSQL as a disposable course dependency. This check proves the database boundary without assuming a Spring application exists.
docker version
docker run --name course-postgres \
-e POSTGRES_PASSWORD=course \
-e POSTGRES_DB=tasks \
-p 5432:5432 \
-d postgres:17-alpine
docker exec course-postgres \
psql -U postgres -d tasks -c "select current_database(), version();"current_database | version\n-----------------+------------------------------\ntasks | PostgreSQL 17.x ...
If port 5432 is already occupied, stop the existing database or map a different host port such as -p 5433:5432. Keep credentials local to the exercise and never reuse course passwords for a real environment. The container can be stopped and started without losing its writable layer, or removed after the lesson when you deliberately want a clean database.
Break it deliberately: Bytecode version mismatch
One of the most frequent errors when adopting modern Spring Boot is bytecode version incompatibility between the compiler and the target runtime environment.
# Compiled on Java 21, then executed under an older Java 17 runtime:
java -jar target/task-service-0.0.1-SNAPSHOT.jarException in thread "main" java.lang.UnsupportedClassVersionError: com/example/TaskApplication has been compiled by a more recent version of the Java Runtime (class file version 65.0), this version of the Java Runtime only recognizes class file versions up to 61.0
| Java LTS Version | Class File Major Version | Spring Boot Support |
|---|---|---|
| Java 8 | 52.0 | Spring Boot 1.x & 2.x only (End of Life) |
| Java 11 | 55.0 | Spring Boot 2.x only (End of Life) |
| Java 17 LTS | 61.0 | Spring Boot 3.0+ minimum baseline |
| Java 21 LTS | 65.0 | Spring Boot 3.2+ recommended course target |
When you see UnsupportedClassVersionError, never guess or blindly reinstall. Identify the two numbers in the error: the compiled version (65 = Java 21) and the executing runtime version (61 = Java 17). Align your shell JAVA_HOME or Docker base image to match Java 21.
Active Recall Checkpoint
If your CI pipeline fails with class file version 65.0 vs 61.0, what needs to be fixed?
Show answer & explanation
The CI execution container is running Java 17 while Maven compiled for Java 21. Update the CI image or runner toolchain to Java 21.
Record one readiness report
Run the commands below from the repository root and save the output with secrets removed. This report turns “it does not work on my machine” into a comparison of concrete versions and reachable services. The HTTP check uses Python only as a temporary static server, so it does not pretend that a Spring Actuator endpoint already exists.
java --version
javac --version
./mvnw --version
git --version
docker version
docker compose version
# Optional independent HTTP smoke test:
python3 -m http.server 8080
# In a second terminal:
curl -i http://localhost:8080/Active Recall Checkpoint
Which result proves Docker is ready: seeing the CLI version or receiving both client and server information from docker version?
Show answer & explanation
Both client and server information. A CLI can be installed while the daemon is stopped or unreachable.
Completion evidence
Guided Hands-On Lab
Code Walkthrough
Run java -version and docker ps from a plain terminal; confirm the JDK reports 21 and the daemon answers with running containers or an empty list.
#!/usr/bin/env bash
set -euo pipefail
echo "=== Workstation Reproducibility Verification ==="
# 1. Verify Java 21 Toolchain
JAVA_VER=$(java --version 2>&1 | head -n 1)
JAVAC_VER=$(javac --version 2>&1 | head -n 1)
echo "[JDK Runtime] $JAVA_VER"
echo "[JDK Compiler] $JAVAC_VER"
if [[ "$JAVA_VER" != *"21."* ]] || [[ "$JAVAC_VER" != *"21."* ]]; then
echo "[ERROR] Course target is Java 21 LTS. Please align JAVA_HOME and PATH."
exit 1
fi
# 2. Verify Maven Wrapper
if [ -f "./mvnw" ]; then
MVN_VER=$(./mvnw --version 2>&1 | head -n 1)
echo "[Maven Wrapper] $MVN_VER"
else
echo "[WARN] ./mvnw not found in current directory; verify repository root."
fi
# 3. Verify Docker Daemon
if docker version >/dev/null 2>&1; then
DOCKER_SERVER=$(docker version --format '{{.Server.Version}}' 2>/dev/null || echo "Reachable")
echo "[Docker Engine] Daemon is reachable (Server v$DOCKER_SERVER)"
else
echo "[ERROR] Docker daemon is not running or unreachable."
exit 1
fi
# 4. Verify PostgreSQL Connectivity
echo "Checking disposable course database container 'course-postgres'..."
if docker ps --filter "name=course-postgres" --format '{{.Names}}' | grep -q "course-postgres"; then
docker exec course-postgres psql -U postgres -d tasks -c "SELECT current_database(), version();"
echo "[PostgreSQL] Connection verified."
else
echo "[ERROR] course-postgres is not running; start the disposable database and rerun this check."
echo "[INFO] Start container: docker run --name course-postgres -e POSTGRES_PASSWORD=course -e POSTGRES_DB=tasks -p 5432:5432 -d postgres:17-alpine"
exit 1
fi
echo "=== All Core Tools Verified for Java 21 & Spring Boot 3.5 ==="Guided LabBuild & Verify Step by Step
A Unix/macOS or Windows PowerShell terminal with Git, JDK 21, and Docker installed.
- 01
Verify JDK 21 compiler and runtime
Run java --version and javac --version. Ensure both report version 21.0.x and point to identical JAVA_HOME.
Check: Both report major version 21; no 17 or 11 mismatch.
- 02
Verify Maven Wrapper and Docker daemon
Run ./mvnw --version and docker version to verify the build wrapper and reachable daemon.
Check: The wrapper prints Maven 3.9+ using Java 21, and docker version reports Server Engine version.
- 03
Launch and query disposable PostgreSQL container
Start the disposable container postgres:17-alpine on port 5432 and run SELECT current_database(), version();
Check: PostgreSQL 17 responds with current_database tasks.
- 04
Observe bytecode version mismatch
Simulate executing a class compiled for Java 21 under Java 17 to recognize UnsupportedClassVersionError (65.0 vs 61.0).
Check: You can immediately diagnose class file version numbers without guessing.
Command
bash verify-workstation.sh
Expected Output
=== Workstation Reproducibility Verification === [JDK Runtime] openjdk 21.x [JDK Compiler] javac 21.x [Docker Engine] Daemon is reachable [PostgreSQL] Connection verified. === All Core Tools Verified for Java 21 & Spring Boot 3.5 ===
[INFO] Scanning for projects... [INFO] -------------------< com.example:order-service >------------------- [INFO] Building order-service 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-surefire-plugin:3.2.5:test (default-test) @ order-service --- [INFO] Running com.example.OrderServiceTest [ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.284 s <<< FAILURE! -- in com.example.OrderServiceTest [ERROR] com.example.OrderServiceTest.testOrderCreation -- Time elapsed: 0.041 s <<< ERROR! java.lang.NullPointerException: Cannot invoke "com.example.OrderRepository.save(Object)" because "this.repository" is null at com.example.OrderService.createOrder(OrderService.java:18) at com.example.OrderServiceTest.testOrderCreation(OrderServiceTest.java:24) [INFO] Results: [INFO] [ERROR] Errors: [ERROR] OrderServiceTest.testOrderCreation:24 » NullPointer Cannot invoke "com.example... [INFO] [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.2.5:test on project order-service: There are test failures. [ERROR] -> [Help 1] [ERROR] BUILD FAILURE
Synthesis & Production Verification
Production Acceptance Criteria
- java -version reports a JDK 21 toolchain
- The IDE project SDK and terminal build both use Java 21
- docker ps reaches a running Docker daemon
Key Takeaway
Reproducible environments eliminate an entire class of bugs: pin the JDK, use project wrappers, verify the Docker daemon, and keep secrets out of committed files.
Architectural & Technical Review
Core Interview Questions
Q01Why can java, javac, Maven, and the IDE report different Java versions?Show answer
They can resolve Java from different PATH entries, JAVA_HOME values, Maven toolchains, or IDE project settings. Compare each tool’s reported runtime and align them; installing another JDK without fixing resolution order does not make the build reproducible.
Q02What does UnsupportedClassVersionError mean?Show answer
The class was compiled to a bytecode version newer than the executing JVM supports. Compare the compiler target, the JVM used by the build, and the JVM launching the application; the failure is a toolchain mismatch rather than a Java source-code bug.
Q03Why prefer the Maven Wrapper over a globally installed Maven?Show answer
The wrapper downloads the exact Maven version the project was built with, so everyone and CI execute identical lifecycle behavior independent of local installs.
Production Pitfalls
Critical Anti-Patterns
- Pitfall: Mixed JDK versions between machines — “Works on my machine” usually means different toolchains. Verify versions before blaming code.
- Pitfall: Assuming Docker is running — docker ps reaching the daemon is the only real proof. Container-dependent steps fail confusingly when it is not running.
- Pitfall: Real credentials in committed configuration — Committed secrets leak permanently. Use dummy dev values locally and supply production secrets at runtime.
Primary Documentation & Specifications
Lesson Primary References
Supplementary Module Resources