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