A RESTful API for managing employee information, built with Spring Boot. This guide will walk you through every step from setup to running and testing the application.
π¨ READ THIS FIRST! If this is your first time running this project after cloning/downloading from GitHub, you MUST complete these setup steps or you will get build errors! π¨
Open your terminal in the project directory and run these commands IN ORDER:
# Step 1: Make gradlew executable
chmod +x gradlew
# Step 2: Update Gradle version to 8.10.2
sed -i '' 's/gradle-7.6.4-bin.zip/gradle-8.10.2-bin.zip/' gradle/wrapper/gradle-wrapper.properties
# Step 3: Update Java version to 21 in build configuration
sed -i '' 's/JavaLanguageVersion.of(17)/JavaLanguageVersion.of(21)/' buildSrc/src/main/groovy/project-conventions.gradle
# Step 4: Clean all cached files
find buildSrc -name "*.class" -type f -delete
rm -rf buildSrc/build
rm -rf .gradle
rm -rf api/build
# Step 5: Set Java 21 as active (CRITICAL!)
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
# Step 6: Verify Java 21 is active
java -version
# Step 7: Stop all Gradle daemons
./gradlew --stop
# Step 8: Format the code
./gradlew spotlessApply
# Step 9: Build the project
./gradlew buildREM Step 1: Update Gradle version
REM Open gradle/wrapper/gradle-wrapper.properties in a text editor
REM Change: gradle-7.6.4-bin.zip to gradle-8.10.2-bin.zip
REM Step 2: Update Java version
REM Open buildSrc/src/main/groovy/project-conventions.gradle
REM Change: JavaLanguageVersion.of(17) to JavaLanguageVersion.of(21)
REM Step 3: Clean cached files
rmdir /s /q buildSrc\build
rmdir /s /q .gradle
rmdir /s /q api\build
REM Step 4: Set Java 21 (adjust path to your Java installation)
set JAVA_HOME=C:\Program Files\Java\jdk-21
set PATH=%JAVA_HOME%\bin;%PATH%
REM Step 5: Verify Java version
java -version
REM Step 6: Stop Gradle daemons
gradlew.bat --stop
REM Step 7: Format and build
gradlew.bat spotlessApply
gradlew.bat buildAfter running the commands above:
After Step 6 (java -version):
openjdk version "21" or "21.x.x"
β If you see Java 21, continue! β If not, install Java 21 first (see Prerequisites)
After Step 9 (./gradlew build):
BUILD SUCCESSFUL in Xs
15 actionable tasks: XX executed
β If you see "BUILD SUCCESSFUL", you're ready to run the app! β If build failed, see Troubleshooting First Time Setup
Error: "Unsupported class file major version 65" or "69"
- You skipped Step 5! Set JAVA_HOME to Java 21 and try again
- Make sure you're using Java 21: java -version
Error: "permission denied: ./gradlew"
- You skipped Step 1! Run: chmod +x gradlew
Error: "spotlessJavaCheck FAILED"
- Run: ./gradlew spotlessApplythen./gradlew build
Still having issues?
- See detailed Troubleshooting section below
- Make sure Java 21 is installed (see Prerequisites)
Once you've completed the setup above successfully, for all future runs you only need:
# Set Java 21 (do this every time you open a new terminal)
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
# Start the server
./gradlew bootRun- Prerequisites
- Initial Setup
- Configuration Steps
- Building the Project
- Running the Application
- Testing the API
- API Documentation
- Troubleshooting
- Project Structure
- 
Java 21 or Higher Check if you have Java installed: java -version You should see: openjdk version "21" or higherDon't have Java 21? Install it: macOS: brew install openjdk@21 sudo ln -sfn /opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-21.jdk Windows: Download from Adoptium or Oracle Linux: sudo apt update sudo apt install openjdk-21-jdk 
- 
Internet Connection (for downloading dependencies) 
- 
Terminal/Command Prompt 
# If you have it as a zip, extract it first
# Then navigate to the project directory
cd entry-level-java-challengechmod +x gradlewWindows users: Skip this step, use gradlew.bat instead of ./gradlew
The project requires Gradle 8.10.2 or higher. Let's ensure it's set correctly:
# Check current Gradle version in the wrapper properties
cat gradle/wrapper/gradle-wrapper.propertiesIf you see gradle-7.6.4 or lower, update it:
# Update to Gradle 8.10.2
sed -i '' 's/gradle-7.6.4-bin.zip/gradle-8.10.2-bin.zip/' gradle/wrapper/gradle-wrapper.propertiesWindows users:
Open gradle/wrapper/gradle-wrapper.properties in a text editor and change:
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
# Update Java version to 21 in the build configuration
sed -i '' 's/JavaLanguageVersion.of(17)/JavaLanguageVersion.of(21)/' buildSrc/src/main/groovy/project-conventions.gradleWindows users:
Open buildSrc/src/main/groovy/project-conventions.gradle and change:
languageVersion = JavaLanguageVersion.of(21)# Remove all buildSrc compiled files
find buildSrc -name "*.class" -type f -delete
# Remove build directories
rm -rf buildSrc/build
rm -rf .gradle
rm -rf api/buildWindows users:
rmdir /s /q buildSrc\build
rmdir /s /q .gradle
rmdir /s /q api\build# macOS/Linux: Set JAVA_HOME to Java 21
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
# Verify it's set correctly
java -versionYou should now see Java 21!
Windows users:
# Find your Java 21 installation path, typically:
# C:\Program Files\Java\jdk-21
# Then set JAVA_HOME:
set JAVA_HOME=C:\Program Files\Java\jdk-21
set PATH=%JAVA_HOME%\bin;%PATH%To make this permanent (Mac/Linux):
Add to your ~/.zshrc or ~/.bashrc:
export JAVA_HOME=$(/usr/libexec/java_home -v 21)./gradlew --stopThis ensures a fresh start with the correct Java version.
The project uses Spotless for code formatting. You must run this before building:
./gradlew spotlessApplyThis will automatically format all Java files according to the project's style guidelines.
./gradlew buildExpected output:
BUILD SUCCESSFUL in Xs
If you see formatting violations:
# Run spotless again
./gradlew spotlessApply
# Then build
./gradlew buildIf build fails with Java version errors:
- Make sure you completed Step 4 in Configuration (setting JAVA_HOME)
- Run java -versionto verify you're using Java 21
- Try ./gradlew --stopthen./gradlew buildagain
./gradlew bootRunYou'll see output like this:
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v3.2.10)
Tomcat initialized with port 8080 (http)
Tomcat started on port 8080 (http) with context path ''
Started EntryLevelJavaChallengeApplication in X.XXX seconds
Important: The terminal will show "IDLE" - this is normal! The server is running and waiting for requests.
Do NOT close this terminal! The server needs to stay running.
Keep the server running in the first terminal, and open a second terminal for testing.
Using Terminal:
curl http://localhost:8080/api/v1/employeeUsing Browser: Open this URL:
http://localhost:8080/api/v1/employee
Expected Response: You'll see 5 pre-loaded employees in JSON format:
[
  {
    "uuid": "58852996-9c72-4406-8286-f9b77b290e9f",
    "firstName": "John",
    "lastName": "Doe",
    "fullName": "John Doe",
    "salary": 75000,
    "age": 30,
    "jobTitle": "Software Engineer",
    "email": "john.doe@company.com",
    "contractHireDate": "2023-10-24T15:12:36.929351Z",
    "contractTerminationDate": null
  },
  ... (4 more employees)
]Step 2a: From the response above, copy any UUID. For example:
58852996-9c72-4406-8286-f9b77b290e9f
Step 2b: Use that UUID in the request:
Using Terminal:
# Replace the UUID with one you copied
curl http://localhost:8080/api/v1/employee/58852996-9c72-4406-8286-f9b77b290e9fUsing Browser:
http://localhost:8080/api/v1/employee/58852996-9c72-4406-8286-f9b77b290e9f
Expected Response:
{
  "uuid": "58852996-9c72-4406-8286-f9b77b290e9f",
  "firstName": "John",
  "lastName": "Doe",
  "fullName": "John Doe",
  "salary": 75000,
  "age": 30,
  "jobTitle": "Software Engineer",
  "email": "john.doe@company.com",
  "contractHireDate": "2023-10-24T15:12:36.929351Z",
  "contractTerminationDate": null
}Using Terminal:
curl -X POST http://localhost:8080/api/v1/employee \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Alice",
    "lastName": "Johnson",
    "salary": 85000,
    "age": 29,
    "jobTitle": "Data Scientist",
    "email": "alice.johnson@company.com"
  }'Expected Response (201 Created):
{
  "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "firstName": "Alice",
  "lastName": "Johnson",
  "fullName": "Alice Johnson",
  "salary": 85000,
  "age": 29,
  "jobTitle": "Data Scientist",
  "email": "alice.johnson@company.com",
  "contractHireDate": "2025-10-23T15:35:00.123456Z",
  "contractTerminationDate": null
}curl http://localhost:8080/api/v1/employeeYou should now see 6 employees including Alice!
In the terminal where the server is running, press:
Ctrl + C
http://localhost:8080/api/v1/employee
| Method | Endpoint | Description | 
|---|---|---|
| GET | /api/v1/employee | Get all employees | 
| GET | /api/v1/employee/{uuid} | Get single employee by UUID | 
| POST | /api/v1/employee | Create new employee | 
Request:
GET /api/v1/employeeResponse: 200 OK
[
  {
    "uuid": "string",
    "firstName": "string",
    "lastName": "string",
    "fullName": "string",
    "salary": 0,
    "age": 0,
    "jobTitle": "string",
    "email": "string",
    "contractHireDate": "2025-10-23T15:12:36.929351Z",
    "contractTerminationDate": null
  }
]Request:
GET /api/v1/employee/{uuid}Response: 200 OK
{
  "uuid": "58852996-9c72-4406-8286-f9b77b290e9f",
  "firstName": "John",
  "lastName": "Doe",
  "fullName": "John Doe",
  "salary": 75000,
  "age": 30,
  "jobTitle": "Software Engineer",
  "email": "john.doe@company.com",
  "contractHireDate": "2023-10-24T15:12:36.929351Z",
  "contractTerminationDate": null
}Response: 404 Not Found
{
  "timestamp": "2025-10-23T15:16:32.625+00:00",
  "status": 404,
  "error": "Not Found",
  "message": "Employee not found with UUID: {uuid}"
}Request:
POST /api/v1/employee
Content-Type: application/jsonRequest Body:
{
  "firstName": "Alice",
  "lastName": "Johnson",
  "salary": 85000,
  "age": 29,
  "jobTitle": "Data Scientist",
  "email": "alice.johnson@company.com"
}Response: 201 Created
{
  "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "firstName": "Alice",
  "lastName": "Johnson",
  "fullName": "Alice Johnson",
  "salary": 85000,
  "age": 29,
  "jobTitle": "Data Scientist",
  "email": "alice.johnson@company.com",
  "contractHireDate": "2025-10-23T15:35:00.123456Z",
  "contractTerminationDate": null
}Response: 400 Bad Request
{
  "timestamp": "2025-10-23T15:16:32.625+00:00",
  "status": 400,
  "error": "Bad Request",
  "message": "First name is required"
}| Field | Type | Required | Validation | 
|---|---|---|---|
| firstName | String | β Yes | Cannot be empty | 
| lastName | String | β Yes | Cannot be empty | 
| email | String | β Yes | Cannot be empty | 
| salary | Integer | β Yes | Must be β₯ 0 | 
| age | Integer | β Yes | Must be between 18-120 | 
| jobTitle | String | β No | Optional | 
- uuid- Automatically generated on creation
- fullName- Created from firstName + lastName
- contractHireDate- Set to current timestamp
- contractTerminationDate- null by default (for active employees)
| Status | Description | 
|---|---|
| 200 OK | Request successful (GET) | 
| 201 Created | Employee created successfully (POST) | 
| 400 Bad Request | Validation error or invalid input | 
| 404 Not Found | Employee with given UUID doesn't exist | 
| 500 Internal Server Error | Server error | 
Solution:
chmod +x gradlewError Message:
Unsupported class file major version XX
Solution:
# Make sure you're using Java 21
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
java -version
# Stop Gradle daemons
./gradlew --stop
# Try again
./gradlew buildError Message:
Task :api:spotlessJavaCheck FAILED
The following files had format violations
Solution:
# Apply formatting
./gradlew spotlessApply
# Build again
./gradlew buildError Message:
Port 8080 is already in use
Solution 1 - Find and Kill the Process:
# Find what's using port 8080
lsof -i :8080
# Kill the process (replace PID with actual process ID)
kill -9 PIDSolution 2 - Change the Port:
Edit api/src/main/resources/application.yml:
server:
  port: 8081Cause: UUIDs are randomly generated when the server starts. They change each time you restart.
Solution:
- Get fresh UUIDs first:
curl http://localhost:8080/api/v1/employee 
- Copy a UUID from the response
- Use that UUID in your request
Check if the build actually completed:
./gradlew clean buildMake sure no other instance is running:
./gradlew --stop
ps aux | grep javaStart with verbose logging:
./gradlew bootRun --infoSolution:
# Stop all daemons
./gradlew --stop
# Check status
./gradlew --status
# Clean everything
rm -rf ~/.gradle/caches/
rm -rf .gradle/
# Try again
./gradlew buildUse gradlew.bat instead of ./gradlew:
gradlew.bat build
gradlew.bat bootRunIf you get path errors:
- Make sure JAVA_HOME is set correctly
- Use full paths if needed
- Run Command Prompt as Administrator
entry-level-java-challenge/
βββ api/
β   βββ src/
β   β   βββ main/
β   β   β   βββ java/com/challenge/api/
β   β   β   β   βββ controller/
β   β   β   β   β   βββ EmployeeController.java      # REST endpoints
β   β   β   β   βββ service/
β   β   β   β   β   βββ EmployeeService.java         # Business logic
β   β   β   β   βββ model/
β   β   β   β   β   βββ Employee.java                # Interface
β   β   β   β   β   βββ EmployeeImpl.java            # Implementation
β   β   β   β   βββ dto/
β   β   β   β   β   βββ CreateEmployeeRequest.java   # Request DTO
β   β   β   β   βββ EntryLevelJavaChallengeApplication.java
β   β   β   βββ resources/
β   β   β       βββ application.yml                   # Configuration
β   β   βββ test/
β   βββ build.gradle
βββ buildSrc/
β   βββ src/main/groovy/
β       βββ project-conventions.gradle                # Build configuration
βββ gradle/
β   βββ wrapper/
β       βββ gradle-wrapper.jar
β       βββ gradle-wrapper.properties                 # Gradle version
βββ gradlew                                          # Gradle wrapper (Unix)
βββ gradlew.bat                                      # Gradle wrapper (Windows)
βββ settings.gradle
βββ README.md
βββββββββββββββββββββββββββββββββββ
β   EmployeeController            β  β REST API Layer
β   @RestController                β     - Handles HTTP requests
β   @RequestMapping               β     - Returns ResponseEntity
ββββββββββββββ¬βββββββββββββββββββββ     - Validates input
             β
             βΌ
βββββββββββββββββββββββββββββββββββ
β   EmployeeService               β  β Business Logic Layer
β   @Service                      β     - Data validation
ββββββββββββββ¬βββββββββββββββββββββ     - UUID generation
             β                           - Business rules
             βΌ
βββββββββββββββββββββββββββββββββββ
β   HashMap (In-Memory Storage)   β  β Data Storage Layer
β   Map<UUID, Employee>           β     - Mock database
βββββββββββββββββββββββββββββββββββ     - Pre-loaded data
Here's a complete example from start to finish:
# ===== TERMINAL 1: Setup and Start Server =====
# 1. Navigate to project
cd entry-level-java-challenge
# 2. Set Java 21
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
java -version
# 3. Clean and setup
./gradlew --stop
rm -rf .gradle buildSrc/build
# 4. Format and build
./gradlew spotlessApply
./gradlew build
# 5. Start server
./gradlew bootRun
# Wait for: "Tomcat started on port 8080"
# ===== TERMINAL 2: Test the API =====
# 1. View all employees
curl http://localhost:8080/api/v1/employee
# 2. Copy a UUID from the output above, then view that employee
curl http://localhost:8080/api/v1/employee/YOUR-UUID-HERE
# 3. Create a new employee
curl -X POST http://localhost:8080/api/v1/employee \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Sarah",
    "lastName": "Connor",
    "salary": 90000,
    "age": 32,
    "jobTitle": "Security Specialist",
    "email": "sarah.connor@company.com"
  }'
# 4. View all employees again (now includes Sarah)
curl http://localhost:8080/api/v1/employee
# 5. Get Sarah's UUID from step 4, then view her details
curl http://localhost:8080/api/v1/employee/SARAH-UUID-HERE
# ===== TERMINAL 1: Stop Server =====
# Press Ctrl + CThe application comes pre-loaded with 5 employees for testing:
| Name | Job Title | Salary | Age | 
|---|---|---|---|
| John Doe | Software Engineer | $75,000 | 30 | 
| Jane Smith | Senior Software Engineer | $95,000 | 35 | 
| Michael Johnson | Engineering Manager | $120,000 | 42 | 
| Emily Williams | Junior Developer | $68,000 | 28 | 
| David Brown | DevOps Engineer | $85,000 | 33 | 
Note: UUIDs are randomly generated each time the server starts.
Before submitting or deploying, verify:
- Java 21 is installed and active
- Gradle wrapper properties updated to 8.10.2
- Java version in build config set to 21
-  Code formatted with Spotless: ./gradlew spotlessApply
-  Build passes: ./gradlew build
-  Server starts successfully: ./gradlew bootRun
- Can view all employees via browser/curl
- Can view single employee by UUID
- Can create new employee via POST
- All endpoints return expected responses
- β RESTful API design with proper HTTP methods
- β Spring Boot 3.2.10 framework
- β In-memory data storage (HashMap)
- β Automatic UUID generation
- β Comprehensive input validation
- β Clean architecture (Controller β Service β Data)
- β Descriptive error messages
- β Code formatting with Spotless
- β Pre-loaded mock data for testing
# Format code
./gradlew spotlessApply
# Build project
./gradlew build
# Clean build
./gradlew clean build
# Run application
./gradlew bootRun
# Stop Gradle daemons
./gradlew --stop
# Check Gradle status
./gradlew --status
# Run with verbose output
./gradlew build --info- 
GET All Employees - Method: GET
- URL: http://localhost:8080/api/v1/employee
 
- 
GET Single Employee - Method: GET
- URL: http://localhost:8080/api/v1/employee/{uuid}
 
- 
POST Create Employee - Method: POST
- URL: http://localhost:8080/api/v1/employee
- Headers: Content-Type: application/json
- Body (raw JSON):
 { "firstName": "Test", "lastName": "User", "salary": 80000, "age": 25, "jobTitle": "Developer", "email": "test@example.com" }
- 
Data Persistence: This application uses in-memory storage. All data resets when you restart the server. 
- 
UUIDs Change: Employee UUIDs are regenerated on each server restart. Always fetch current UUIDs before querying specific employees. 
- 
Java Version: The project REQUIRES Java 21. It will not work with older versions. 
- 
Gradle Version: Must use Gradle 8.10.2 or higher for Java 21 support. 
- 
Code Formatting: Always run ./gradlew spotlessApplybefore building to avoid formatting violations.
- 
Port 8080: Make sure port 8080 is available. Change in application.ymlif needed.
This is an educational project for ReliaQuest's Entry-Level Java Challenge.
If you've followed all the steps and can:
- β Start the server without errors
- β View all employees in your browser
- β Get a single employee by UUID
- β Create new employees via POST request
Congratulations! Your Employee Management API is working perfectly! π
Built with β€οΈ using Spring Boot
For questions or issues, refer to the Troubleshooting section above.