Skip to content

Commit c798b9d

Browse files
author
Yuriy Bezsonov
committed
feat(apps): enhance Java 25 CDS and AOT Dockerfiles with improved classpath handling
1 parent 70f3f57 commit c798b9d

3 files changed

Lines changed: 115 additions & 31 deletions

File tree

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,58 @@
11
FROM public.ecr.aws/docker/library/maven:3-amazoncorretto-25-al2023 AS builder
22

3+
COPY ./pom.xml ./pom.xml
4+
COPY src ./src/
5+
6+
RUN mvn clean package -DskipTests -ntp && mv target/store-spring-1.0.0-exec.jar app.jar
7+
8+
FROM public.ecr.aws/docker/library/amazoncorretto:25-al2023 AS trainer
9+
10+
COPY --from=builder app.jar app.jar
11+
12+
# Explode fat jar for CDS training
13+
RUN mkdir -p /ex && (cd /ex && jar -xf /app.jar) && \
14+
mkdir -p /opt/app/training /opt/app/lib && \
15+
(cd /ex/BOOT-INF/classes && jar -cf /opt/app/training/classes.jar .) && \
16+
cp -r /ex/BOOT-INF/lib/* /opt/app/lib/
17+
18+
# Create sorted classpath for deterministic ordering between training and runtime
19+
RUN ls /opt/app/lib/*.jar | sort | tr '\n' ':' | sed 's/:$//' > /opt/app/lib-cp.txt
20+
21+
ENV MAIN_CLASS="com.unicorn.store.StoreApplication"
22+
23+
# Optional: pass DB props for training to capture Hibernate/JDBC classes
324
ARG SPRING_DATASOURCE_URL
4-
ENV SPRING_DATASOURCE_URL=$SPRING_DATASOURCE_URL
525
ARG SPRING_DATASOURCE_USERNAME
6-
ENV SPRING_DATASOURCE_USERNAME=$SPRING_DATASOURCE_USERNAME
726
ARG SPRING_DATASOURCE_PASSWORD
8-
ENV SPRING_DATASOURCE_PASSWORD=$SPRING_DATASOURCE_PASSWORD
9-
10-
COPY ./pom.xml ./pom.xml
11-
COPY src ./src/
1227

13-
RUN mvn clean package -DskipTests -ntp && mv target/store-spring-1.0.0-exec.jar store-spring.jar
28+
# If DB credentials provided, use them; otherwise exclude DB auto-config
29+
ENV TRAINING_JAVA_OPTS_DEFAULT="-Dspring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration -Dspring.main.lazy-initialization=true"
1430

1531
# CDS training - dump class archive after Spring context initialization
16-
RUN java -XX:ArchiveClassesAtExit=/app.jsa \
17-
-Dspring.context.exit=onRefresh \
18-
-jar /store-spring.jar || true && \
19-
test -s /app.jsa
32+
RUN set -e; \
33+
if [ -n "${SPRING_DATASOURCE_URL}" ]; then \
34+
OPTS="-Dspring.datasource.url=${SPRING_DATASOURCE_URL} -Dspring.datasource.username=${SPRING_DATASOURCE_USERNAME} -Dspring.datasource.password=${SPRING_DATASOURCE_PASSWORD}"; \
35+
else \
36+
OPTS="${TRAINING_JAVA_OPTS_DEFAULT}"; \
37+
fi; \
38+
java -XX:ArchiveClassesAtExit=/opt/app/app.jsa \
39+
-Dspring.context.exit=onRefresh ${OPTS} \
40+
-cp "/opt/app/training/classes.jar:$(cat /opt/app/lib-cp.txt)" \
41+
${MAIN_CLASS} || true && \
42+
test -s /opt/app/app.jsa
2043

2144
FROM public.ecr.aws/docker/library/amazoncorretto:25-al2023
2245

2346
RUN yum install -y shadow-utils && \
2447
groupadd --system spring -g 1000 && \
2548
adduser spring -u 1000 -g 1000
2649

27-
COPY --from=builder --chown=1000:1000 /store-spring.jar /store-spring.jar
28-
COPY --from=builder --chown=1000:1000 /app.jsa /app.jsa
50+
COPY --from=trainer --chown=1000:1000 /opt/app/training/classes.jar /opt/app/training/classes.jar
51+
COPY --from=trainer --chown=1000:1000 /opt/app/lib/ /opt/app/lib/
52+
COPY --from=trainer --chown=1000:1000 /opt/app/app.jsa /opt/app/app.jsa
53+
COPY --from=trainer --chown=1000:1000 /opt/app/lib-cp.txt /opt/app/lib-cp.txt
2954

3055
USER 1000:1000
3156
EXPOSE 8080
3257

33-
ENTRYPOINT ["java", "-XX:SharedArchiveFile=/app.jsa", "-jar", "-Dserver.port=8080", "/store-spring.jar"]
58+
ENTRYPOINT ["sh", "-c", "exec java -XX:SharedArchiveFile=/opt/app/app.jsa -Dserver.port=8080 -cp /opt/app/training/classes.jar:$(cat /opt/app/lib-cp.txt) com.unicorn.store.StoreApplication"]

apps/dockerfiles-java25/Dockerfile.07-aot

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,32 @@ RUN ls /opt/app/lib/*.jar | sort | tr '\n' ':' | sed 's/:$//' > /opt/app/lib-cp.
2323
ENV MAIN_CLASS="com.unicorn.store.StoreApplication"
2424

2525
# Optional: pass DB props for training to capture Hibernate/JDBC classes
26-
# docker build --build-arg TRAINING_JAVA_OPTS="-Dspring.datasource.url=... -Dspring.datasource.username=... -Dspring.datasource.password=..." ...
27-
ARG TRAINING_JAVA_OPTS=""
28-
ENV TRAINING_JAVA_OPTS=${TRAINING_JAVA_OPTS}
26+
ARG SPRING_DATASOURCE_URL
27+
ARG SPRING_DATASOURCE_USERNAME
28+
ARG SPRING_DATASOURCE_PASSWORD
29+
30+
# If DB credentials provided, use them; otherwise exclude DB auto-config
2931
ENV TRAINING_JAVA_OPTS_DEFAULT="-Dspring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration -Dspring.main.lazy-initialization=true"
3032

3133
# Record AOT configuration
3234
RUN set -e; \
33-
OPTS="${TRAINING_JAVA_OPTS}"; [ -z "${OPTS}" ] && OPTS="${TRAINING_JAVA_OPTS_DEFAULT}"; \
35+
if [ -n "${SPRING_DATASOURCE_URL}" ]; then \
36+
OPTS="-Dspring.datasource.url=${SPRING_DATASOURCE_URL} -Dspring.datasource.username=${SPRING_DATASOURCE_USERNAME} -Dspring.datasource.password=${SPRING_DATASOURCE_PASSWORD}"; \
37+
else \
38+
OPTS="${TRAINING_JAVA_OPTS_DEFAULT}"; \
39+
fi; \
3440
java -XX:AOTMode=record -XX:AOTConfiguration=/app.aotconf \
3541
-cp "/opt/app/training/classes.jar:$(cat /opt/app/lib-cp.txt)" \
3642
-Dspring.context.exit=onRefresh ${OPTS} ${MAIN_CLASS} || true && \
3743
test -s /app.aotconf
3844

3945
# Create AOT cache
4046
RUN set -e; \
41-
OPTS="${TRAINING_JAVA_OPTS}"; [ -z "${OPTS}" ] && OPTS="${TRAINING_JAVA_OPTS_DEFAULT}"; \
47+
if [ -n "${SPRING_DATASOURCE_URL}" ]; then \
48+
OPTS="-Dspring.datasource.url=${SPRING_DATASOURCE_URL} -Dspring.datasource.username=${SPRING_DATASOURCE_USERNAME} -Dspring.datasource.password=${SPRING_DATASOURCE_PASSWORD}"; \
49+
else \
50+
OPTS="${TRAINING_JAVA_OPTS_DEFAULT}"; \
51+
fi; \
4252
java -XX:AOTMode=create -XX:AOTConfiguration=/app.aotconf \
4353
-XX:AOTCache=/opt/app/app.aot \
4454
-cp "/opt/app/training/classes.jar:$(cat /opt/app/lib-cp.txt)" \

infra/scripts/deploy/test-optimizations.sh

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ cleanup() {
4545
"${APP_DIR}/src/main/java/com/unicorn/store/data/UnicornPublisher.java"
4646
fi
4747

48-
# Revert deployment to baseline if in deploy mode (disabled for testing)
49-
# if [[ "$DEPLOY_MODE" == true && -n "$ACCOUNT_ID" ]]; then
50-
# log_info "Reverting deployment to :latest..."
51-
# local ecr_uri="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_NAME}"
52-
# kubectl set image deployment/unicorn-store-spring \
53-
# unicorn-store-spring="${ecr_uri}:latest" -n unicorn-store-spring 2>/dev/null || true
54-
# fi
48+
# Revert deployment to baseline if --revert flag is set
49+
if [[ "$REVERT_MODE" == true && "$DEPLOY_MODE" == true && -n "$ACCOUNT_ID" ]]; then
50+
log_info "Reverting deployment to :latest..."
51+
local ecr_uri="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_NAME}"
52+
kubectl set image deployment/unicorn-store-spring \
53+
unicorn-store-spring="${ecr_uri}:latest" -n unicorn-store-spring 2>/dev/null || true
54+
fi
5555

5656
exit $exit_code
5757
}
@@ -74,11 +74,13 @@ METHODS=(
7474

7575
# Parse arguments
7676
DEPLOY_MODE=false
77+
REVERT_MODE=false
7778
ONLY_METHOD=""
7879

7980
while [[ $# -gt 0 ]]; do
8081
case $1 in
8182
--deploy) DEPLOY_MODE=true; shift ;;
83+
--revert) REVERT_MODE=true; shift ;;
8284
--only) ONLY_METHOD="$2"; shift 2 ;;
8385
*) log_error "Unknown option: $1"; exit 1 ;;
8486
esac
@@ -113,8 +115,43 @@ needs_code_change() {
113115
[[ "$1" == "09-crac" ]]
114116
}
115117

116-
# Start PostgreSQL for training
118+
# Database configuration - try AWS first, fallback to local Docker
119+
USE_AWS_DB=false
120+
SPRING_DATASOURCE_URL=""
121+
SPRING_DATASOURCE_USERNAME=""
122+
SPRING_DATASOURCE_PASSWORD=""
123+
124+
init_db_config() {
125+
log_info "Checking for AWS database configuration..."
126+
127+
# Try to get AWS database credentials
128+
local aws_url aws_secret
129+
aws_url=$(aws ssm get-parameter --name workshop-db-connection-string --no-cli-pager 2>/dev/null | jq --raw-output '.Parameter.Value' 2>/dev/null) || true
130+
aws_secret=$(aws secretsmanager get-secret-value --secret-id workshop-db-secret --no-cli-pager 2>/dev/null | jq --raw-output '.SecretString' 2>/dev/null) || true
131+
132+
if [[ -n "$aws_url" && "$aws_url" != "null" && -n "$aws_secret" && "$aws_secret" != "null" ]]; then
133+
SPRING_DATASOURCE_URL="$aws_url"
134+
SPRING_DATASOURCE_USERNAME=$(echo "$aws_secret" | jq -r .username)
135+
SPRING_DATASOURCE_PASSWORD=$(echo "$aws_secret" | jq -r .password)
136+
137+
if [[ -n "$SPRING_DATASOURCE_USERNAME" && "$SPRING_DATASOURCE_USERNAME" != "null" ]]; then
138+
USE_AWS_DB=true
139+
log_info "Using AWS RDS database for training"
140+
return 0
141+
fi
142+
fi
143+
144+
log_info "AWS database not available, will use local Docker PostgreSQL"
145+
USE_AWS_DB=false
146+
}
147+
148+
# Start PostgreSQL for training (only if not using AWS DB)
117149
start_build_db() {
150+
if [[ "$USE_AWS_DB" == true ]]; then
151+
log_info "Using AWS RDS database (no local DB needed)"
152+
return 0
153+
fi
154+
118155
log_info "Starting PostgreSQL for training..."
119156
docker rm -f build-postgres 2>/dev/null || true
120157
docker run -d --name build-postgres \
@@ -128,10 +165,19 @@ start_build_db() {
128165
until docker exec build-postgres pg_isready -U unicorn -d unicornstore >/dev/null 2>&1; do
129166
sleep 1
130167
done
168+
169+
# Set local DB credentials
170+
SPRING_DATASOURCE_URL="jdbc:postgresql://host.docker.internal:5432/unicornstore"
171+
SPRING_DATASOURCE_USERNAME="unicorn"
172+
SPRING_DATASOURCE_PASSWORD="unicorn"
173+
131174
log_info "PostgreSQL ready"
132175
}
133176

134177
stop_build_db() {
178+
if [[ "$USE_AWS_DB" == true ]]; then
179+
return 0
180+
fi
135181
docker rm -f build-postgres 2>/dev/null || true
136182
}
137183

@@ -188,12 +234,12 @@ build_image() {
188234
crac_pre_build
189235
fi
190236

191-
# Start DB if needed
237+
# Start DB if needed and set build args (all methods use same SPRING_DATASOURCE_* args)
192238
if needs_db "$tag"; then
193239
start_build_db
194-
build_args="--build-arg SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/unicornstore"
195-
build_args="${build_args} --build-arg SPRING_DATASOURCE_USERNAME=unicorn"
196-
build_args="${build_args} --build-arg SPRING_DATASOURCE_PASSWORD=unicorn"
240+
build_args="--build-arg SPRING_DATASOURCE_URL=${SPRING_DATASOURCE_URL}"
241+
build_args="${build_args} --build-arg SPRING_DATASOURCE_USERNAME=${SPRING_DATASOURCE_USERNAME}"
242+
build_args="${build_args} --build-arg SPRING_DATASOURCE_PASSWORD=${SPRING_DATASOURCE_PASSWORD}"
197243
fi
198244

199245
# Build with --progress=plain for cleaner logs
@@ -373,6 +419,9 @@ rm -rf "${OUTPUT_DIR}"
373419
mkdir -p "${OUTPUT_DIR}"
374420
log_info "Output: ${OUTPUT_DIR}"
375421

422+
# Initialize database configuration (AWS or local Docker)
423+
init_db_config
424+
376425
# Initialize deploy mode
377426
if [[ "$DEPLOY_MODE" == true ]]; then
378427
log_info "Deploy mode enabled - will push to ECR and deploy to EKS"

0 commit comments

Comments
 (0)