diff --git a/judoscale-core/src/main/java/com/judoscale/core/Adapter.java b/judoscale-core/src/main/java/com/judoscale/core/Adapter.java index fab766e..ed95964 100644 --- a/judoscale-core/src/main/java/com/judoscale/core/Adapter.java +++ b/judoscale-core/src/main/java/com/judoscale/core/Adapter.java @@ -11,14 +11,16 @@ public final class Adapter { private final String name; private final String version; + private final String runtimeVersion; /** - * Creates an Adapter with the specified name and version. + * Creates an Adapter with the specified name, version, and runtime version. * * @param name the adapter name (e.g., "judoscale-spring-boot", "judoscale-spring-boot-2"), must not be null * @param version the adapter version, must not be null + * @param runtimeVersion the runtime version (e.g., Spring Boot version), may be null */ - public Adapter(String name, String version) { + public Adapter(String name, String version, String runtimeVersion) { if (name == null) { throw new IllegalArgumentException("Adapter name must not be null"); } @@ -27,6 +29,7 @@ public Adapter(String name, String version) { } this.name = name; this.version = version; + this.runtimeVersion = runtimeVersion; } /** @@ -47,21 +50,31 @@ public String version() { return version; } + /** + * Returns the runtime version (e.g., Spring Boot version). + * + * @return the runtime version, or null if not set + */ + public String runtimeVersion() { + return runtimeVersion; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Adapter adapter = (Adapter) o; - return Objects.equals(name, adapter.name) && Objects.equals(version, adapter.version); + return Objects.equals(name, adapter.name) && Objects.equals(version, adapter.version) + && Objects.equals(runtimeVersion, adapter.runtimeVersion); } @Override public int hashCode() { - return Objects.hash(name, version); + return Objects.hash(name, version, runtimeVersion); } @Override public String toString() { - return "Adapter{name='" + name + "', version='" + version + "'}"; + return "Adapter{name='" + name + "', version='" + version + "', runtimeVersion='" + runtimeVersion + "'}"; } } diff --git a/judoscale-core/src/main/java/com/judoscale/core/ReportBuilder.java b/judoscale-core/src/main/java/com/judoscale/core/ReportBuilder.java index 9dcf968..cae6a7c 100644 --- a/judoscale-core/src/main/java/com/judoscale/core/ReportBuilder.java +++ b/judoscale-core/src/main/java/com/judoscale/core/ReportBuilder.java @@ -57,6 +57,7 @@ public static String buildReportJson(List metrics, Collection a for (Adapter adapter : adapters) { ObjectNode adapterNode = objectMapper.createObjectNode(); adapterNode.put("adapter_version", adapter.version()); + adapterNode.put("runtime_version", adapter.runtimeVersion()); adaptersNode.set(adapter.name(), adapterNode); } root.set("adapters", adaptersNode); diff --git a/judoscale-core/src/test/java/com/judoscale/core/AdapterTest.java b/judoscale-core/src/test/java/com/judoscale/core/AdapterTest.java index 1d1082c..992b0b1 100644 --- a/judoscale-core/src/test/java/com/judoscale/core/AdapterTest.java +++ b/judoscale-core/src/test/java/com/judoscale/core/AdapterTest.java @@ -8,35 +8,47 @@ class AdapterTest { @Test - void createsAdapterWithNameAndVersion() { - Adapter adapter = new Adapter("judoscale-spring-boot", "1.0.0"); + void createsAdapterWithAllFields() { + Adapter adapter = new Adapter("judoscale-spring-boot", "1.0.0", "3.2.2"); assertThat(adapter.name()).isEqualTo("judoscale-spring-boot"); assertThat(adapter.version()).isEqualTo("1.0.0"); + assertThat(adapter.runtimeVersion()).isEqualTo("3.2.2"); + } + + @Test + void allowsNullRuntimeVersion() { + Adapter adapter = new Adapter("judoscale-spring-boot", "1.0.0", null); + + assertThat(adapter.runtimeVersion()).isNull(); } @Test void throwsExceptionWhenNameIsNull() { - assertThatThrownBy(() -> new Adapter(null, "1.0.0")) + assertThatThrownBy(() -> new Adapter(null, "1.0.0", "3.2.2")) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Adapter name must not be null"); } @Test void throwsExceptionWhenVersionIsNull() { - assertThatThrownBy(() -> new Adapter("judoscale-spring-boot", null)) + assertThatThrownBy(() -> new Adapter("judoscale-spring-boot", null, "3.2.2")) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Adapter version must not be null"); } @Test void equalsAndHashCodeWorkCorrectly() { - Adapter adapter1 = new Adapter("judoscale-spring-boot", "1.0.0"); - Adapter adapter2 = new Adapter("judoscale-spring-boot", "1.0.0"); - Adapter adapter3 = new Adapter("judoscale-spring-boot-2", "1.0.0"); + Adapter adapter1 = new Adapter("judoscale-spring-boot", "1.0.0", "3.2.2"); + Adapter adapter2 = new Adapter("judoscale-spring-boot", "1.0.0", "3.2.2"); + Adapter adapter3 = new Adapter("judoscale-spring-boot-2", "1.0.0", "3.2.2"); + Adapter adapter4 = new Adapter("judoscale-spring-boot", "1.0.0", "2.7.0"); + Adapter adapter5 = new Adapter("judoscale-spring-boot", "1.0.0", null); assertThat(adapter1).isEqualTo(adapter2); assertThat(adapter1.hashCode()).isEqualTo(adapter2.hashCode()); assertThat(adapter1).isNotEqualTo(adapter3); + assertThat(adapter1).isNotEqualTo(adapter4); + assertThat(adapter1).isNotEqualTo(adapter5); } } diff --git a/judoscale-core/src/test/java/com/judoscale/core/ReportBuilderTest.java b/judoscale-core/src/test/java/com/judoscale/core/ReportBuilderTest.java index 21e96f0..8b49e99 100644 --- a/judoscale-core/src/test/java/com/judoscale/core/ReportBuilderTest.java +++ b/judoscale-core/src/test/java/com/judoscale/core/ReportBuilderTest.java @@ -11,7 +11,7 @@ class ReportBuilderTest { - private static final Adapter TEST_ADAPTER = new Adapter("judoscale-test", "1.0.0"); + private static final Adapter TEST_ADAPTER = new Adapter("judoscale-test", "1.0.0", "3.2.1"); @Test void buildReportJsonFormatsMetricsCorrectly() { @@ -31,6 +31,7 @@ void buildReportJsonFormatsMetricsCorrectly() { assertThat(json).contains("\"adapters\":"); assertThat(json).contains("\"judoscale-test\""); assertThat(json).contains("\"adapter_version\":\"1.0.0\""); + assertThat(json).contains("\"runtime_version\":\"3.2.1\""); } @Test @@ -81,8 +82,8 @@ void buildReportJsonHandlesEmptyContainer() { @Test void buildReportJsonSupportsMultipleAdapters() { - Adapter springBootAdapter = new Adapter("judoscale-spring-boot", "1.0.0"); - Adapter sidekiqAdapter = new Adapter("judoscale-sidekiq", "2.0.0"); + Adapter springBootAdapter = new Adapter("judoscale-spring-boot", "1.0.0", "3.2.2"); + Adapter sidekiqAdapter = new Adapter("judoscale-sidekiq", "2.0.0", null); List adapters = Arrays.asList(springBootAdapter, sidekiqAdapter); String json = ReportBuilder.buildReportJson(Collections.emptyList(), adapters, "web.1"); @@ -91,6 +92,8 @@ void buildReportJsonSupportsMultipleAdapters() { assertThat(json).contains("\"judoscale-sidekiq\""); assertThat(json).contains("\"adapter_version\":\"1.0.0\""); assertThat(json).contains("\"adapter_version\":\"2.0.0\""); + assertThat(json).contains("\"runtime_version\":\"3.2.2\""); + assertThat(json).contains("\"runtime_version\":null"); } @Test diff --git a/judoscale-spring-boot-2-starter/src/main/java/com/judoscale/spring/JudoscaleApiClient.java b/judoscale-spring-boot-2-starter/src/main/java/com/judoscale/spring/JudoscaleApiClient.java index 929bdac..9bcc74e 100644 --- a/judoscale-spring-boot-2-starter/src/main/java/com/judoscale/spring/JudoscaleApiClient.java +++ b/judoscale-spring-boot-2-starter/src/main/java/com/judoscale/spring/JudoscaleApiClient.java @@ -5,6 +5,7 @@ import com.judoscale.core.Metric; import com.judoscale.core.ReportBuilder; import org.apache.http.client.config.RequestConfig; +import org.springframework.boot.SpringBootVersion; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; @@ -30,7 +31,8 @@ public class JudoscaleApiClient implements ApiClient, Closeable { private static final int MAX_RETRIES = 3; private static final Adapter ADAPTER = new Adapter( "judoscale-spring-boot-2", - ReportBuilder.loadAdapterVersion(JudoscaleApiClient.class) + ReportBuilder.loadAdapterVersion(JudoscaleApiClient.class), + SpringBootVersion.getVersion() ); private final JudoscaleConfig config; diff --git a/judoscale-spring-boot-starter/src/main/java/com/judoscale/spring/JudoscaleApiClient.java b/judoscale-spring-boot-starter/src/main/java/com/judoscale/spring/JudoscaleApiClient.java index fd56cb1..d2069cc 100644 --- a/judoscale-spring-boot-starter/src/main/java/com/judoscale/spring/JudoscaleApiClient.java +++ b/judoscale-spring-boot-starter/src/main/java/com/judoscale/spring/JudoscaleApiClient.java @@ -6,6 +6,7 @@ import com.judoscale.core.ReportBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringBootVersion; import java.io.IOException; import java.net.URI; @@ -24,7 +25,8 @@ public class JudoscaleApiClient implements ApiClient { private static final int MAX_RETRIES = 3; private static final Adapter ADAPTER = new Adapter( "judoscale-spring-boot", - ReportBuilder.loadAdapterVersion(JudoscaleApiClient.class) + ReportBuilder.loadAdapterVersion(JudoscaleApiClient.class), + SpringBootVersion.getVersion() ); private final JudoscaleConfig config;