diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index fa87ad7d..15fbe3d2 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.1/apache-maven-3.6.1-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar diff --git a/adoptopenjdk-api-v3-frontend/pom.xml b/adoptopenjdk-api-v3-frontend/pom.xml index 6022125f..3db63db8 100644 --- a/adoptopenjdk-api-v3-frontend/pom.xml +++ b/adoptopenjdk-api-v3-frontend/pom.xml @@ -13,6 +13,10 @@ adoptopenjdk-api-v3-frontend + + io.quarkus + quarkus-smallrye-metrics + io.quarkus quarkus-resteasy @@ -28,7 +32,6 @@ io.quarkus quarkus-smallrye-openapi - ${quarkus.version} org.jetbrains.kotlinx @@ -99,19 +102,21 @@ io.mockk mockk - 1.9.3 test io.rest-assured rest-assured - 4.0.0 test io.rest-assured json-path - 4.0.0 + test + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr353 test diff --git a/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/metrics/DayMeter.kt b/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/metrics/DayMeter.kt new file mode 100644 index 00000000..adb6e11f --- /dev/null +++ b/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/metrics/DayMeter.kt @@ -0,0 +1,94 @@ +package net.adoptopenjdk.api.v3.metrics + +import io.smallrye.metrics.app.Clock +import io.smallrye.metrics.app.EWMA +import org.eclipse.microprofile.metrics.Counting +import org.eclipse.microprofile.metrics.Metric +import java.lang.management.ManagementFactory +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicLong +import java.util.concurrent.atomic.LongAdder + +/* +Based on MeterImpl from smallrye-metrics + */ +class DayMeter : Metric, Counting { + + companion object { + private val TICK_INTERVAL = TimeUnit.MINUTES.toNanos(1) + + private const val INTERVAL = 1.0 + private const val MINUTES_PER_DAY = 60.0 * 24.0 + private const val MINUTES_PER_WEEK = 7.0 * 60.0 * 24.0 + private const val MINUTES_PER_HOUR = 1.0 + private const val M0_ALPHA = 2.0 / ((MINUTES_PER_HOUR / INTERVAL) + 1.0) + private const val M1_ALPHA = 2.0 / ((MINUTES_PER_DAY / INTERVAL) + 1.0) + private const val M7_ALPHA = 2.0 / ((MINUTES_PER_WEEK / INTERVAL) + 1.0) + } + + private val h1Rate = EWMA(M0_ALPHA, INTERVAL.toLong(), TimeUnit.MINUTES) + private val d1Rate = EWMA(M1_ALPHA, INTERVAL.toLong(), TimeUnit.MINUTES) + private val d7Rate = EWMA(M7_ALPHA, INTERVAL.toLong(), TimeUnit.MINUTES) + + private val count = LongAdder() + private val startTime: Long = 0 + private val clock: Clock = Clock.defaultClock() + private val lastTick: AtomicLong = AtomicLong(clock.tick) + + fun mark() { + tickIfNecessary() + count.add(1) + h1Rate.update(1) + d1Rate.update(1) + d7Rate.update(1) + } + + private fun tickIfNecessary() { + val oldTick = lastTick.get() + val newTick = clock.tick + val age = newTick - oldTick + if (age > TICK_INTERVAL) { + val newIntervalStartTick = newTick - age % TICK_INTERVAL + if (lastTick.compareAndSet(oldTick, newIntervalStartTick)) { + val requiredTicks = age / TICK_INTERVAL + for (i in 0 until requiredTicks) { + h1Rate.tick() + d1Rate.tick() + d7Rate.tick() + } + } + } + } + + override fun getCount(): Long { + return count.sum() + } + + fun getOneHourRate(): Double { + tickIfNecessary() + return h1Rate.getRate(TimeUnit.MINUTES) + } + + fun getMeanRate(): Double { + return if (count.toLong() == 0L) { + 0.0 + } else { + val elapsed = clock.tick - startTime + count.toDouble() / elapsed.toDouble() * TimeUnit.MINUTES.toNanos(1) + } + } + + fun getTotalRequestsPerHour(): Double { + return count.toDouble() / ((1 + TimeUnit.MILLISECONDS.toMinutes(ManagementFactory.getRuntimeMXBean().uptime).toDouble()) / 60.0) + } + + fun getOneDayRate(): Double { + tickIfNecessary() + return d1Rate.getRate(TimeUnit.MINUTES) + } + + fun getSevenDayRate(): Double { + tickIfNecessary() + return d7Rate.getRate(TimeUnit.MINUTES) + } +} diff --git a/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/metrics/StatsInterceptor.kt b/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/metrics/StatsInterceptor.kt new file mode 100644 index 00000000..556c9dfd --- /dev/null +++ b/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/metrics/StatsInterceptor.kt @@ -0,0 +1,69 @@ +package net.adoptopenjdk.api.v3.metrics + +import io.smallrye.metrics.ExtendedMetadata +import io.smallrye.metrics.MetricRegistries +import org.eclipse.microprofile.metrics.Gauge +import org.eclipse.microprofile.metrics.MetricRegistry +import org.eclipse.microprofile.metrics.MetricType +import org.eclipse.microprofile.metrics.MetricUnits +import org.jboss.resteasy.core.interception.jaxrs.PostMatchContainerRequestContext +import java.lang.management.ManagementFactory +import java.util.concurrent.TimeUnit +import javax.ws.rs.container.ContainerRequestContext +import javax.ws.rs.container.ContainerRequestFilter +import javax.ws.rs.ext.Provider + +@Provider +class StatsInterceptor : ContainerRequestFilter { + + private val stats = mutableMapOf() + private val all = DayMeter() + + init { + val metricRegistry = MetricRegistries + .get(MetricRegistry.Type.APPLICATION) + + addGuage(metricRegistry, "1 hour rate") { all.getOneHourRate() } + addGuage(metricRegistry, "1 day rate") { all.getOneDayRate() } + addGuage(metricRegistry, "7 day rate") { all.getSevenDayRate() } + addGuage(metricRegistry, "1 hour rate total") { all.getTotalRequestsPerHour() } + addGuage(metricRegistry, "mean") { all.getMeanRate() } + addGuage(metricRegistry, "count") { all.count } + addGuage(metricRegistry, "upTimeMin") { TimeUnit.MILLISECONDS.toMinutes(ManagementFactory.getRuntimeMXBean().uptime).toDouble() } + } + + override fun filter(requestContext: ContainerRequestContext?) { + if (requestContext is PostMatchContainerRequestContext) { + val path = requestContext.resourceMethod.method.declaringClass.simpleName + "." + requestContext.resourceMethod.method.name + + var stat = stats[path] + if (stat == null) { + stat = DayMeter() + stats[path] = stat + + val metricRegistry = MetricRegistries + .get(MetricRegistry.Type.APPLICATION) + + addGuage(metricRegistry, "$path 1 hour rate") { stat.getOneHourRate() } + addGuage(metricRegistry, "$path 1 day rate") { stat.getOneDayRate() } + addGuage(metricRegistry, "$path 7 day rate") { stat.getSevenDayRate() } + addGuage(metricRegistry, "$path 1 hour rate total") { stat.getTotalRequestsPerHour() } + addGuage(metricRegistry, "$path mean") { stat.getMeanRate() } + addGuage(metricRegistry, "$path count") { stat.count } + } + stat.mark() + all.mark() + } + } + + private fun addGuage(metricRegistry: MetricRegistry, name: String, getter: () -> Number) { + metricRegistry.register(ExtendedMetadata(name, + MetricType.GAUGE, + MetricUnits.NONE, + name, + true + ), object : Gauge { + override fun getValue(): Number = getter() + }) + } +} diff --git a/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/AssetsResource.kt b/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/AssetsResource.kt index 4a494e9e..f3c4163e 100644 --- a/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/AssetsResource.kt +++ b/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/AssetsResource.kt @@ -42,11 +42,11 @@ class AssetsResource { @GET @Path("/feature_releases/{feature_version}/{release_type}") @Operation(summary = "Returns release information", - description = "List of information about builds that match the current query" + description = "List of information about builds that match the current query" ) @APIResponses(value = [ APIResponse(responseCode = "200", description = "search results matching criteria", - content = [Content(schema = Schema(type = SchemaType.ARRAY, implementation = Release::class))] + content = [Content(schema = Schema(type = SchemaType.ARRAY, implementation = Release::class))] ), APIResponse(responseCode = "400", description = "bad input parameter") ] @@ -57,7 +57,7 @@ class AssetsResource { release_type: ReleaseType?, @Parameter(name = "feature_version", description = OpenApiDocs.FEATURE_RELEASE, required = true, - schema = Schema(defaultValue = "8", type = SchemaType.INTEGER) + schema = Schema(defaultValue = "8", type = SchemaType.INTEGER) ) @PathParam("feature_version") version: Int?, @@ -87,20 +87,20 @@ class AssetsResource { vendor: Vendor?, @Parameter(name = "project", description = "Project", schema = Schema(defaultValue = "jdk", - enumeration = ["jdk", "valhalla", "metropolis", "jfr"], required = false + enumeration = ["jdk", "valhalla", "metropolis", "jfr"], required = false ), required = false ) @QueryParam("project") project: Project?, @Parameter(name = "page_size", description = "Pagination page size", - schema = Schema(defaultValue = "10", type = SchemaType.INTEGER), required = false + schema = Schema(defaultValue = "10", type = SchemaType.INTEGER), required = false ) @QueryParam("page_size") pageSize: Int?, @Parameter(name = "page", description = "Pagination page number", - schema = Schema(defaultValue = "0", type = SchemaType.INTEGER), required = false + schema = Schema(defaultValue = "0", type = SchemaType.INTEGER), required = false ) @QueryParam("page") page: Int?, @@ -133,11 +133,11 @@ class AssetsResource { @GET @Path("/version/{version}") @Operation(summary = "Returns release information about the specified version.", - description = "List of information about builds that match the current query " + description = "List of information about builds that match the current query " ) @APIResponses(value = [ APIResponse(responseCode = "200", description = "search results matching criteria", - content = [Content(schema = Schema(type = SchemaType.ARRAY, implementation = Release::class))] + content = [Content(schema = Schema(type = SchemaType.ARRAY, implementation = Release::class))] ), APIResponse(responseCode = "400", description = "bad input parameter") ] @@ -172,7 +172,7 @@ class AssetsResource { vendor: Vendor?, @Parameter(name = "project", description = "Project", schema = Schema(defaultValue = "jdk", - enumeration = ["jdk", "valhalla", "metropolis", "jfr"], required = false + enumeration = ["jdk", "valhalla", "metropolis", "jfr"], required = false ), required = false ) @QueryParam("project") @@ -187,13 +187,13 @@ class AssetsResource { release_type: ReleaseType?, @Parameter(name = "page_size", description = "Pagination page size", - schema = Schema(defaultValue = "20", type = SchemaType.INTEGER), required = false + schema = Schema(defaultValue = "20", type = SchemaType.INTEGER), required = false ) @QueryParam("page_size") pageSize: Int?, @Parameter(name = "page", description = "Pagination page number", - schema = Schema(defaultValue = "0", type = SchemaType.INTEGER), required = false + schema = Schema(defaultValue = "0", type = SchemaType.INTEGER), required = false ) @QueryParam("page") page: Int?, @@ -246,7 +246,7 @@ class AssetsResource { fun getLatestAssets( @Parameter(name = "feature_version", description = OpenApiDocs.FEATURE_RELEASE, required = true, - schema = Schema(defaultValue = "8", type = SchemaType.INTEGER) + schema = Schema(defaultValue = "8", type = SchemaType.INTEGER) ) @PathParam("feature_version") version: Int, diff --git a/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/BinaryResource.kt b/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/BinaryResource.kt index 5fc85602..74a055b0 100644 --- a/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/BinaryResource.kt +++ b/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/BinaryResource.kt @@ -90,6 +90,7 @@ class BinaryResource { @Context request: Request, + @Parameter(hidden = true, required = false) @HeaderParam("User-Agent") userAgent: String ): Response { diff --git a/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/SortOrder.kt b/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/SortOrder.kt index c364a476..2c0ad443 100644 --- a/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/SortOrder.kt +++ b/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/SortOrder.kt @@ -3,7 +3,7 @@ package net.adoptopenjdk.api.v3.dataSources import org.eclipse.microprofile.openapi.annotations.enums.SchemaType import org.eclipse.microprofile.openapi.annotations.media.Schema -@Schema(type = SchemaType.STRING, enumeration = ["ASC", "DESC"], example = "DESC") +@Schema(type = SchemaType.STRING, enumeration = ["ASC", "DESC"], defaultValue = "DESC") enum class SortOrder { ASC, DESC; diff --git a/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/Architecture.kt b/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/Architecture.kt index 142d07a5..2a69e13c 100644 --- a/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/Architecture.kt +++ b/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/Architecture.kt @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonCreator import org.eclipse.microprofile.openapi.annotations.enums.SchemaType import org.eclipse.microprofile.openapi.annotations.media.Schema -@Schema(type = SchemaType.STRING, enumeration = ["x64", "x32", "ppc64", "ppc64le", "s390x", "aarch64", "arm", "sparcv9"], example = "x64") +@Schema(type = SchemaType.STRING, enumeration = ["x64", "x32", "ppc64", "ppc64le", "s390x", "aarch64", "arm", "sparcv9"]) enum class Architecture : FileNameMatcher { x64, x32("x86-32"), diff --git a/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/JvmImpl.kt b/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/JvmImpl.kt index 1cbefde7..1a734075 100644 --- a/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/JvmImpl.kt +++ b/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/JvmImpl.kt @@ -3,7 +3,7 @@ package net.adoptopenjdk.api.v3.models import org.eclipse.microprofile.openapi.annotations.enums.SchemaType import org.eclipse.microprofile.openapi.annotations.media.Schema -@Schema(type = SchemaType.STRING, enumeration = ["hotspot", "openj9"], example = "hotspot") +@Schema(type = SchemaType.STRING, enumeration = ["hotspot", "openj9"]) enum class JvmImpl : FileNameMatcher { hotspot, openj9; diff --git a/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/OperatingSystem.kt b/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/OperatingSystem.kt index c93a208b..9be12ae8 100644 --- a/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/OperatingSystem.kt +++ b/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/OperatingSystem.kt @@ -3,7 +3,7 @@ package net.adoptopenjdk.api.v3.models import org.eclipse.microprofile.openapi.annotations.enums.SchemaType import org.eclipse.microprofile.openapi.annotations.media.Schema -@Schema(type = SchemaType.STRING, enumeration = ["linux", "windows", "mac", "solaris", "aix"], example = "linux") +@Schema(type = SchemaType.STRING, enumeration = ["linux", "windows", "mac", "solaris", "aix"]) enum class OperatingSystem : FileNameMatcher { linux("LinuxLH"), windows("win"), diff --git a/adoptopenjdk-api-v3-persistance/pom.xml b/adoptopenjdk-api-v3-persistance/pom.xml index 459af9a8..a49c8aa2 100644 --- a/adoptopenjdk-api-v3-persistance/pom.xml +++ b/adoptopenjdk-api-v3-persistance/pom.xml @@ -33,6 +33,14 @@ org.glassfish jakarta.json + + org.mongodb + mongodb-driver-reactivestreams + + + org.mongodb + mongodb-driver-core + diff --git a/adoptopenjdk-api-v3-updater/pom.xml b/adoptopenjdk-api-v3-updater/pom.xml index 91f47d1c..05b4de7d 100644 --- a/adoptopenjdk-api-v3-updater/pom.xml +++ b/adoptopenjdk-api-v3-updater/pom.xml @@ -20,7 +20,6 @@ org.apache.httpcomponents httpasyncclient - 4.1.4