Add library-management module: console app evolved into a Spring Boot 4 REST API
Stage 2 console classes and the shared LoanPolicy, then a Spring Boot 4.1.2 + JPA API (H2, @Version optimistic locking, problem+json errors). Tests write output/ transcripts while asserting the same numbers; scripts regenerate everything. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b4DPL6dtpFg4U4LHc2
This commit is contained in:
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# Drives a running API (scripts/run.sh --detach) with curl: the same story as the console demo,
|
||||
# against a real servlet container. Dates in the output are "today", so they differ per run.
|
||||
set -uo pipefail
|
||||
BASE=${BASE:-http://localhost:8080/api}
|
||||
call() { # method path [json]
|
||||
echo "> $1 $2${3:+ $3}"
|
||||
if [ -n "${3:-}" ]; then
|
||||
curl -s -X "$1" "$BASE$2" -H 'Content-Type: application/json' -d "$3" -w '\n< HTTP %{http_code}\n\n'
|
||||
else
|
||||
curl -s -X "$1" "$BASE$2" -w '\n< HTTP %{http_code}\n\n'
|
||||
fi
|
||||
}
|
||||
call POST /books '{"isbn":"978-0134685991","title":"Effective Java","author":"Joshua Bloch","copies":2}'
|
||||
call POST /books '{"isbn":"978-0596009205","title":"Head First Java","author":"Kathy Sierra","copies":1}'
|
||||
call POST /books '{"isbn":"","title":"","author":"Nobody","copies":0}'
|
||||
call POST /members '{"name":"Asha","email":"[email protected]"}'
|
||||
call POST /members '{"name":"Ravi","email":"not-an-email"}'
|
||||
call POST /members '{"name":"Ravi","email":"[email protected]"}'
|
||||
call POST /loans '{"memberId":1,"bookId":2}'
|
||||
call POST /loans '{"memberId":2,"bookId":2}'
|
||||
call POST /loans/1/return
|
||||
call GET /books
|
||||
@@ -0,0 +1,14 @@
|
||||
add-book 978-0134685991|Effective Java|Joshua Bloch|2
|
||||
add-book 978-0596009205|Head First Java|Kathy Sierra|1
|
||||
add-book 978-0134685991|Effective Java|Joshua Bloch|2
|
||||
add-member Asha
|
||||
add-member Ravi
|
||||
borrow 978-0596009205 1 2026-11-01
|
||||
borrow 978-0596009205 2 2026-11-01
|
||||
borrow 978-0134685991 1 2026-11-01
|
||||
report
|
||||
return 1 2026-11-20
|
||||
return 1 2026-11-21
|
||||
borrow 978-0596009205 2 2026-11-21
|
||||
report
|
||||
quit
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# Starts the API with NO application.properties, to capture the warning Spring Boot prints when
|
||||
# spring.jpa.open-in-view is left at its default. Our real config sets it to false explicitly.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
mvn -B -q -DskipTests package
|
||||
mkdir -p /tmp/empty-config
|
||||
timeout 25 java -jar target/library-management-1.0.0.jar --spring.config.location=file:/tmp/empty-config/ --server.port=0 2>&1 \
|
||||
| grep -E 'open-in-view|Started LibraryApplication' | sed -E 's/^[0-9T:.+-]+ +//' || true
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerates every file in output/. Needs JDK 25 and Maven; the first run must be online.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
rm -f output/*.txt
|
||||
|
||||
# 01: the single-file console version (the copy attached to the article lives in the WordPress media library)
|
||||
mkdir -p /tmp/lm && cp scripts/demo-input.txt /tmp/lm/
|
||||
echo "01 console (structured) ..."
|
||||
mvn -B -q -DskipTests compile
|
||||
java -cp target/classes com.ankurm.library.console.ConsoleApp < scripts/demo-input.txt 2>/dev/null > output/01-console-structured.txt
|
||||
|
||||
# 02-06: written by the tests themselves, so each file is also an assertion
|
||||
echo "02-06 tests ..."
|
||||
mvn -B -q test
|
||||
|
||||
# 07: the real server, driven with curl
|
||||
echo "07 curl demo ..."
|
||||
scripts/run.sh --detach
|
||||
scripts/api-demo.sh > output/07-curl-demo.txt
|
||||
scripts/stop.sh
|
||||
|
||||
# 08: the Boot default we override
|
||||
echo "08 open-in-view warning ..."
|
||||
scripts/open-in-view-warning.sh > output/08-open-in-view-warning.txt
|
||||
|
||||
# 09: the SQL one borrow produces
|
||||
echo "09 borrow SQL ..."
|
||||
scripts/sql-trace.sh > output/09-borrow-sql.txt
|
||||
ls output
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# Starts the REST API on :8080 (in-memory H2, so every start is a clean library).
|
||||
# Usage: scripts/run.sh foreground
|
||||
# scripts/run.sh --detach background, waits until /api/books answers
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
mvn -B -q -DskipTests package
|
||||
if [ "${1:-}" = "--detach" ]; then
|
||||
setsid nohup java -jar target/library-management-1.0.0.jar > /tmp/library-api.log 2>&1 < /dev/null &
|
||||
for _ in $(seq 1 60); do curl -s -o /dev/null localhost:8080/api/books && exit 0; sleep 1; done
|
||||
echo "API did not start; see /tmp/library-api.log" >&2; exit 1
|
||||
fi
|
||||
exec java -jar target/library-management-1.0.0.jar
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
# Starts the API with Hibernate SQL logging, performs one borrow, and prints only the SQL that
|
||||
# the borrow produced. Look for "version" in the UPDATE: that is optimistic locking at work.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
mvn -B -q -DskipTests package
|
||||
LOG=/tmp/library-sql.log
|
||||
setsid nohup java -jar target/library-management-1.0.0.jar --server.port=8081 --logging.level.org.hibernate.SQL=DEBUG > "$LOG" 2>&1 < /dev/null &
|
||||
for _ in $(seq 1 60); do curl -s -o /dev/null localhost:8081/api/books && break; sleep 1; done
|
||||
J='Content-Type: application/json'
|
||||
curl -s -o /dev/null -H "$J" -d '{"isbn":"978-0134685991","title":"Effective Java","author":"Joshua Bloch","copies":2}' localhost:8081/api/books
|
||||
curl -s -o /dev/null -H "$J" -d '{"name":"Asha","email":"[email protected]"}' localhost:8081/api/members
|
||||
START=$(wc -l < "$LOG")
|
||||
curl -s -o /dev/null -H "$J" -d '{"memberId":1,"bookId":1}' localhost:8081/api/loans
|
||||
sleep 1
|
||||
tail -n +"$((START + 1))" "$LOG" | grep 'org.hibernate.SQL' | sed -E 's/^.*org.hibernate.SQL +: //'
|
||||
scripts/stop.sh
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# Stops the API started by run.sh --detach. Only processes whose command is "java" match, so
|
||||
# this script can never match (and kill) the shell that is running it.
|
||||
for p in $(ps -eo pid,comm,args | awk '$2 == "java" && /library-management-1.0.0.jar/ {print $1}'); do kill "$p"; done
|
||||
Reference in New Issue
Block a user