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

40 min

Lesson 3 of 7

1d streak

The 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.

ToolResponsibilityProof it is ready
JDK 21Compile and run Javajava and javac report major version 21
IDE/editorEdit, navigate, debug, and run testsProject SDK and language level are 21
Maven WrapperResolve dependencies and run the build./mvnw --version uses Java 21
GitRecord and compare source changesgit --version succeeds
DockerRun disposable supporting servicesdocker version reaches the daemon
HTTP clientSend and inspect requestsA local fixture returns headers and a body
PostgreSQLPractice relational data and transactionspsql or a container accepts a query

One canonical path

Module 0 uses Maven and PostgreSQL. Gradle and MySQL are valid technologies, but mixing them into the primary path makes beginner failures harder to localize.

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.

verify-java.txt
java --version
javac --version

# macOS and Linux
echo "$JAVA_HOME"

# Windows PowerShell
$env:JAVA_HOME
Terminal / Diagnostic Output
openjdk 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.

verify-build.sh
git --version
./mvnw --version
./mvnw clean verify

Commit .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

If the IDE succeeds but ./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.

verify-postgres.sh
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();"
Terminal / Diagnostic Output
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.

run-failure.sh
# Compiled on Java 21, then executed under an older Java 17 runtime:
java -jar target/task-service-0.0.1-SNAPSHOT.jar
Terminal / Diagnostic Output
Exception 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 VersionClass File Major VersionSpring Boot Support
Java 852.0Spring Boot 1.x & 2.x only (End of Life)
Java 1155.0Spring Boot 2.x only (End of Life)
Java 17 LTS61.0Spring Boot 3.0+ minimum baseline
Java 21 LTS65.0Spring 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.

readiness-report.sh
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

Keep the version report, a successful wrapper build, one PostgreSQL query, and one inspected HTTP response. These are the starting facts for every later environment diagnosis.

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.

verify-workstation.sh
#!/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 ==="
JDK 21 is the course teaching target; both javac (compiler) and java (runtime) must agree on major version 21.
The Maven Wrapper (mvnw) ensures the exact Maven build distribution is pinned regardless of developer machine variations.
Docker daemon status must report both client and server versions; an installed CLI alone does not prove containers can run.
Guided LabBuild & Verify Step by Step

A Unix/macOS or Windows PowerShell terminal with Git, JDK 21, and Docker installed.

  1. 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.

  2. 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.

  3. 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.

  4. 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 ===
Simulated terminal replay — matched to lesson verificationJDK 21 / Maven 3.9
spring-boot-lab@localhost:~
$bash verify-workstation.sh#failingCondition
[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
Exit Code: 1
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

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