Skip to content

Commit bba870b

Browse files
committed
Java 25 and Spring Boot 4 upgrade
1 parent 600654e commit bba870b

19 files changed

Lines changed: 558 additions & 310 deletions

apps/unicorn-store-spring/Dockerfile

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

33
RUN yum install -y shadow-utils
44

@@ -14,4 +14,4 @@ RUN adduser spring -u 1000 -g 1000
1414
USER 1000:1000
1515

1616
EXPOSE 8080
17-
ENTRYPOINT ["java","-jar","-Dserver.port=8080","/store-spring.jar"]
17+
ENTRYPOINT ["java","-jar","-Dserver.port=8080","/store-spring.jar"]

apps/unicorn-store-spring/pom.xml

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<parent>
88
<groupId>org.springframework.boot</groupId>
99
<artifactId>spring-boot-starter-parent</artifactId>
10-
<version>3.5.8</version>
10+
<version>4.0.0</version>
1111
<relativePath/>
1212
<!-- lookup parent from repository -->
1313
</parent>
@@ -17,18 +17,48 @@
1717
<name>store</name>
1818
<description>Unicorn storage service</description>
1919
<properties>
20-
<java.version>21</java.version>
21-
<maven.compiler.source>21</maven.compiler.source>
22-
<maven.compiler.target>21</maven.compiler.target>
20+
<java.version>25</java.version>
21+
<maven.compiler.source>25</maven.compiler.source>
22+
<maven.compiler.target>25</maven.compiler.target>
23+
<maven.compiler.release>25</maven.compiler.release>
2324
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
2425
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
26+
<testcontainers.version>1.20.4</testcontainers.version>
2527
</properties>
28+
29+
<build>
30+
<plugins>
31+
<plugin>
32+
<groupId>org.apache.maven.plugins</groupId>
33+
<artifactId>maven-compiler-plugin</artifactId>
34+
<configuration>
35+
<compilerArgs>
36+
</compilerArgs>
37+
</configuration>
38+
</plugin>
39+
<plugin>
40+
<groupId>org.apache.maven.plugins</groupId>
41+
<artifactId>maven-surefire-plugin</artifactId>
42+
<version>3.5.4</version>
43+
<configuration>
44+
<argLine>
45+
--add-opens java.base/java.lang=ALL-UNNAMED
46+
--add-opens java.base/java.util=ALL-UNNAMED
47+
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
48+
--add-opens java.base/java.text=ALL-UNNAMED
49+
--add-opens java.desktop/java.awt.font=ALL-UNNAMED
50+
</argLine>
51+
</configuration>
52+
</plugin>
53+
</plugins>
54+
</build>
55+
2656
<dependencyManagement>
2757
<dependencies>
2858
<dependency>
2959
<groupId>software.amazon.awssdk</groupId>
3060
<artifactId>bom</artifactId>
31-
<version>2.39.2</version>
61+
<version>2.33.4</version>
3262
<type>pom</type>
3363
<scope>import</scope>
3464
</dependency>
@@ -58,6 +88,11 @@
5888
<groupId>org.springframework.boot</groupId>
5989
<artifactId>spring-boot-starter-actuator</artifactId>
6090
</dependency>
91+
<!-- Jackson dependencies -->
92+
<dependency>
93+
<groupId>com.fasterxml.jackson.core</groupId>
94+
<artifactId>jackson-databind</artifactId>
95+
</dependency>
6196
<!-- https://mvnrepository.com/artifact/io.micrometer/micrometer-registry-prometheus -->
6297
<dependency>
6398
<groupId>io.micrometer</groupId>
@@ -105,6 +140,11 @@
105140
<artifactId>spring-boot-starter-test</artifactId>
106141
<scope>test</scope>
107142
</dependency>
143+
<dependency>
144+
<groupId>org.springframework.boot</groupId>
145+
<artifactId>spring-boot-starter-webflux</artifactId>
146+
<scope>test</scope>
147+
</dependency>
108148
<dependency>
109149
<groupId>org.springframework.boot</groupId>
110150
<artifactId>spring-boot-testcontainers</artifactId>
@@ -115,23 +155,26 @@
115155
<dependency>
116156
<groupId>org.testcontainers</groupId>
117157
<artifactId>junit-jupiter</artifactId>
158+
<version>${testcontainers.version}</version>
118159
<scope>test</scope>
119160
</dependency>
120161
<dependency>
121162
<groupId>org.testcontainers</groupId>
122163
<artifactId>postgresql</artifactId>
164+
<version>${testcontainers.version}</version>
123165
<scope>test</scope>
124166
</dependency>
125167
<dependency>
126168
<groupId>org.testcontainers</groupId>
127169
<artifactId>localstack</artifactId>
170+
<version>${testcontainers.version}</version>
128171
<scope>test</scope>
129172
</dependency>
130173

131-
<!-- Rest assured dependencies -->
174+
<!-- Test dependencies -->
132175
<dependency>
133-
<groupId>io.rest-assured</groupId>
134-
<artifactId>rest-assured</artifactId>
176+
<groupId>com.h2database</groupId>
177+
<artifactId>h2</artifactId>
135178
<scope>test</scope>
136179
</dependency>
137180
</dependencies>
@@ -161,17 +204,17 @@
161204
<dependency>
162205
<groupId>io.opentelemetry.instrumentation</groupId>
163206
<artifactId>opentelemetry-aws-sdk-2.2</artifactId>
164-
<version>2.15.0-alpha</version>
207+
<version>2.22.0-alpha</version>
165208
</dependency>
166209
<dependency>
167210
<groupId>io.opentelemetry.contrib</groupId>
168211
<artifactId>opentelemetry-aws-xray</artifactId>
169-
<version>1.46.0</version>
212+
<version>1.52.0</version>
170213
</dependency>
171214
<dependency>
172215
<groupId>io.opentelemetry.contrib</groupId>
173216
<artifactId>opentelemetry-aws-xray-propagator</artifactId>
174-
<version>1.46.0-alpha</version>
217+
<version>1.52.0-alpha</version>
175218
</dependency>
176219
</dependencies>
177220
</profile>
@@ -204,7 +247,7 @@
204247
<metadataRepository>
205248
<enabled>true</enabled>
206249
</metadataRepository>
207-
<requiredVersion>21</requiredVersion><!-- Spring Boot parent defines 22.3 -->
250+
<requiredVersion>25</requiredVersion><!-- Spring Boot parent defines 22.3 -->
208251
<buildArgs>
209252
<arg>--verbose</arg>
210253
<arg>
@@ -250,16 +293,16 @@
250293
<image>
251294
<name>unicorn-store-spring:latest</name>
252295
<buildpacks>
253-
<buildpack>paketobuildpacks/amazon-corretto:9.0.1</buildpack>
254-
<buildpack>paketobuildpacks/java:17.6.0</buildpack>
255-
<buildpack>paketobuildpacks/syft:2.7.0</buildpack>
256-
<buildpack>paketobuildpacks/spring-boot:5.32.0</buildpack>
257-
<buildpack>paketobuildpacks/executable-jar:6.12.0</buildpack>
296+
<buildpack>paketobuildpacks/amazon-corretto:9.3.2</buildpack>
297+
<buildpack>paketobuildpacks/java:20.2.0</buildpack>
298+
<buildpack>paketobuildpacks/syft:2.24.0</buildpack>
299+
<buildpack>paketobuildpacks/spring-boot:5.33.5</buildpack>
300+
<buildpack>paketobuildpacks/executable-jar:6.13.4</buildpack>
258301
</buildpacks>
259302
<env>
260303
<SPRING_DATASOURCE_URL>${env.SPRING_DATASOURCE_URL}</SPRING_DATASOURCE_URL>
261304
<SPRING_DATASOURCE_PASSWORD>${env.SPRING_DATASOURCE_PASSWORD}</SPRING_DATASOURCE_PASSWORD>
262-
<BP_JVM_VERSION>21</BP_JVM_VERSION>
305+
<BP_JVM_VERSION>25</BP_JVM_VERSION>
263306
<BP_JVM_CDS_ENABLED>true</BP_JVM_CDS_ENABLED>
264307
<BP_SPRING_AOT_ENABLED>true</BP_SPRING_AOT_ENABLED>
265308
</env>
@@ -296,7 +339,7 @@
296339
<version>3.5.1</version>
297340
<configuration>
298341
<from>
299-
<image>public.ecr.aws/docker/library/amazoncorretto:21-alpine</image>
342+
<image>public.ecr.aws/docker/library/amazoncorretto:25-alpine</image>
300343
</from>
301344
<container>
302345
<user>1000</user>
@@ -307,4 +350,4 @@
307350
</build>
308351
</profile>
309352
</profiles>
310-
</project>
353+
</project>

apps/unicorn-store-spring/src/main/java/com/unicorn/store/config/MonitoringConfig.java

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import com.fasterxml.jackson.databind.ObjectMapper;
55
import io.micrometer.core.instrument.MeterRegistry;
66
import io.micrometer.core.instrument.config.MeterFilter;
7-
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
8-
import org.springframework.context.annotation.Bean;
7+
import jakarta.annotation.PostConstruct;
8+
import org.springframework.beans.factory.annotation.Autowired;
99
import org.springframework.context.annotation.Configuration;
1010

1111
import java.io.File;
@@ -25,44 +25,45 @@ public class MonitoringConfig {
2525
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
2626
private static final File NAMESPACE_FILE = new File("/var/run/secrets/kubernetes.io/serviceaccount/namespace");
2727

28-
@Bean
29-
public MeterRegistryCustomizer<MeterRegistry> meterRegistryCustomizer() {
30-
return registry -> {
31-
String clusterType = System.getenv("ECS_CONTAINER_METADATA_URI_V4") != null ? "ecs" : "eks";
32-
String cluster = clusterType.equals("ecs") ? extractClusterNameFromMetadata().orElse("unknown") : Optional.ofNullable(System.getenv("CLUSTER")).orElse("unknown");
33-
String containerName = "unicorn-store-spring";
34-
String taskOrPodId = extractTaskOrPodId().orElse("unknown");
35-
String namespace = clusterType.equals("eks") ? readNamespaceFile().orElse("default") : "";
36-
37-
// Get the container/pod IP address
38-
String ipAddress = getContainerOrPodIp().orElse("unknown");
39-
40-
registry.config().commonTags(
41-
"cluster", cluster,
42-
"cluster_type", clusterType,
43-
"container_name", containerName,
44-
"task_pod_id", taskOrPodId,
45-
"instance", ipAddress, // Keep this for backward compatibility
46-
"container_ip", ipAddress // Add this new tag that won't be overwritten
47-
);
48-
49-
if (!namespace.isEmpty()) {
50-
registry.config().commonTags("namespace", namespace);
51-
} else {
52-
registry.config().commonTags("namespace", "<no namespace>");
53-
}
28+
@Autowired
29+
private MeterRegistry meterRegistry;
30+
31+
@PostConstruct
32+
public void configureMeterRegistry() {
33+
String clusterType = System.getenv("ECS_CONTAINER_METADATA_URI_V4") != null ? "ecs" : "eks";
34+
String cluster = clusterType.equals("ecs") ? extractClusterNameFromMetadata().orElse("unknown") : Optional.ofNullable(System.getenv("CLUSTER")).orElse("unknown");
35+
String containerName = "unicorn-store-spring";
36+
String taskOrPodId = extractTaskOrPodId().orElse("unknown");
37+
String namespace = clusterType.equals("eks") ? readNamespaceFile().orElse("default") : "";
38+
39+
// Get the container/pod IP address
40+
String ipAddress = getContainerOrPodIp().orElse("unknown");
41+
42+
meterRegistry.config().commonTags(
43+
"cluster", cluster,
44+
"cluster_type", clusterType,
45+
"container_name", containerName,
46+
"task_pod_id", taskOrPodId,
47+
"instance", ipAddress, // Keep this for backward compatibility
48+
"container_ip", ipAddress // Add this new tag that won't be overwritten
49+
);
50+
51+
if (!namespace.isEmpty()) {
52+
meterRegistry.config().commonTags("namespace", namespace);
53+
} else {
54+
meterRegistry.config().commonTags("namespace", "<no namespace>");
55+
}
5456

55-
registry.config().meterFilter(
56-
MeterFilter.deny(id ->
57-
id.getName().equals("jvm.gc.pause") &&
58-
!id.getTags().stream().allMatch(tag ->
59-
tag.getKey().equals("action") ||
60-
tag.getKey().equals("cause") ||
61-
tag.getKey().equals("gc")
62-
)
63-
)
64-
);
65-
};
57+
meterRegistry.config().meterFilter(
58+
MeterFilter.deny(id ->
59+
id.getName().equals("jvm.gc.pause") &&
60+
!id.getTags().stream().allMatch(tag ->
61+
tag.getKey().equals("action") ||
62+
tag.getKey().equals("cause") ||
63+
tag.getKey().equals("gc")
64+
)
65+
)
66+
);
6667
}
6768

6869
private Optional<String> extractTaskOrPodId() {
@@ -165,4 +166,4 @@ private Optional<String> getContainerOrPodIp() {
165166
return Optional.empty();
166167
}
167168
}
168-
}
169+
}

apps/unicorn-store-spring/src/main/java/com/unicorn/store/controller/ThreadManagementController.java

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,50 @@ public ThreadManagementController(ThreadGeneratorService threadGeneratorService)
1616

1717
@PostMapping("/start")
1818
public ResponseEntity<String> startThreads(@RequestParam(defaultValue = "500") int count) {
19-
try {
20-
threadGeneratorService.startThreads(count);
21-
return ResponseEntity.ok("Successfully started " + count + " threads");
22-
} catch (IllegalStateException e) {
23-
return ResponseEntity.badRequest().body(e.getMessage());
19+
var result = tryStartThreads(count);
20+
if (result instanceof Success success) {
21+
return ResponseEntity.ok(success.message());
22+
} else if (result instanceof Failure failure) {
23+
return ResponseEntity.badRequest().body(failure.error());
2424
}
25+
return ResponseEntity.internalServerError().body("Unknown result type");
2526
}
2627

2728
@PostMapping("/stop")
2829
public ResponseEntity<String> stopThreads() {
29-
try {
30-
threadGeneratorService.stopThreads();
31-
return ResponseEntity.ok("Successfully stopped all threads");
32-
} catch (IllegalStateException e) {
33-
return ResponseEntity.badRequest().body(e.getMessage());
30+
var result = tryStopThreads();
31+
if (result instanceof Success success) {
32+
return ResponseEntity.ok(success.message());
33+
} else if (result instanceof Failure failure) {
34+
return ResponseEntity.badRequest().body(failure.error());
3435
}
36+
return ResponseEntity.internalServerError().body("Unknown result type");
3537
}
3638

3739
@GetMapping("/count")
3840
public ResponseEntity<Integer> getThreadCount() {
3941
return ResponseEntity.ok(threadGeneratorService.getActiveThreadCount());
4042
}
41-
}
43+
44+
private Result tryStartThreads(int count) {
45+
try {
46+
threadGeneratorService.startThreads(count);
47+
return new Success("Successfully started " + count + " threads");
48+
} catch (IllegalStateException e) {
49+
return new Failure(e.getMessage());
50+
}
51+
}
52+
53+
private Result tryStopThreads() {
54+
try {
55+
threadGeneratorService.stopThreads();
56+
return new Success("Successfully stopped all threads");
57+
} catch (IllegalStateException e) {
58+
return new Failure(e.getMessage());
59+
}
60+
}
61+
62+
private interface Result {}
63+
private record Success(String message) implements Result {}
64+
private record Failure(String error) implements Result {}
65+
}

0 commit comments

Comments
 (0)