diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index 5a082b65..00d7aafb 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -27,13 +27,14 @@ dependencies { api(libs.micronaut.inject) annotationProcessor(libs.micronaut.inject.java) api(libs.micronaut.context) - api(libs.micronaut.http.client) - api(libs.micronaut.validation) - annotationProcessor(libs.micronaut.validation.processor) - api(libs.micronaut.serde.jsonp) - compileOnlyApi(libs.jakarta.jsonb) - annotationProcessor(libs.micronaut.serde.processor) + //todo re-add validation + api(libs.avaje.http.client) + api(libs.avaje.http.api) + annotationProcessor(libs.avaje.http.client.generator) + + implementation(libs.avaje.jsonb) + annotationProcessor(libs.avaje.jsonb.generator) testImplementation(libs.junit.jupiter) testRuntimeOnly(libs.junit.platform.launcher) diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/command/WhitelistCommand.java b/core/common/src/main/java/org/geysermc/floodgate/core/command/WhitelistCommand.java index bcfc5ddc..77226e25 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/command/WhitelistCommand.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/command/WhitelistCommand.java @@ -27,7 +27,7 @@ package org.geysermc.floodgate.core.command; import static org.geysermc.floodgate.core.platform.command.Placeholder.literal; -import io.micronaut.http.client.exceptions.HttpClientResponseException; +import io.avaje.http.client.HttpException; import jakarta.inject.Inject; import jakarta.inject.Singleton; import java.util.UUID; @@ -129,14 +129,14 @@ public class WhitelistCommand implements FloodgateCommand { xboxClient.xuidByGamertag(name) .whenComplete((result, error) -> { if (error != null) { - if (!(error instanceof HttpClientResponseException exception)) { + if (!(error instanceof HttpException exception)) { sender.sendMessage(Message.API_UNAVAILABLE); error.printStackTrace(); return; } sender.sendMessage(CommonCommandMessage.UNEXPECTED_ERROR); - //todo proper non-200 status handler + //todo proper non-200 status handler, for everything tbh // var response = exception.getResponse().getBody(UnsuccessfulResponse.class); // var message = // response.isPresent() ? diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/HttpClientFactory.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/HttpClientFactory.java new file mode 100644 index 00000000..472a70e4 --- /dev/null +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/HttpClientFactory.java @@ -0,0 +1,72 @@ +package org.geysermc.floodgate.core.http; + +import io.avaje.http.client.HttpClient; +import io.avaje.http.client.JsonbBodyAdapter; +import io.micronaut.context.annotation.Bean; +import io.micronaut.context.annotation.Factory; +import io.micronaut.context.annotation.Property; +import io.micronaut.context.annotation.Value; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import java.util.concurrent.Executor; +import org.geysermc.floodgate.core.http.api.GlobalApiClient; +import org.geysermc.floodgate.core.http.downloads.DownloadClient; +import org.geysermc.floodgate.core.http.link.GlobalLinkClient; +import org.geysermc.floodgate.core.http.minecraft.MinecraftClient; +import org.geysermc.floodgate.core.http.mojang.SessionServerClient; +import org.geysermc.floodgate.core.http.xbox.XboxClient; +import org.geysermc.floodgate.isolation.library.LibraryManager; + +@Factory +public class HttpClientFactory { + @Inject LibraryManager manager; + @Named("commonPool") Executor pool; + + @Bean + GlobalApiClient globalApiClient(@Property(name = "http.baseUrl.api") String url) { + return create(url, GlobalApiClient.class); + } + + @Bean + GlobalLinkClient globalLinkClient(@Value("${http.baseUrl.api}/v2/link") String url) { + return create(url, GlobalLinkClient.class); + } + + @Bean + XboxClient xboxClient(@Value("${http.baseUrl.api}/v2/xbox") String url) { + return create(url, XboxClient.class); + } + + @Bean + DownloadClient downloadClient(@Property(name = "http.baseUrl.download") String url) { + return create(url, DownloadClient.class); + } + + @Bean + MinecraftClient minecraftClient() { + return create("https://api.minecraftservices.com/minecraft", MinecraftClient.class); + } + + @Bean + SessionServerClient sessionServerClient() { + return create("https://sessionserver.mojang.com/session/minecraft", SessionServerClient.class); + } + + private T create(String baseUrl, Class type) { + var thread = Thread.currentThread(); + // for both Avaje's jsonb and http-client to work with it's generated classes. + // The generated classes are in the inner-jar (isolated), and the current context is on the outer-jar + var context = thread.getContextClassLoader(); + thread.setContextClassLoader(manager.classLoader()); + + var result = HttpClient.builder() + .baseUrl(baseUrl) + .bodyAdapter(new JsonbBodyAdapter()) + .executor(pool) + .build() + .create(type); + + thread.setContextClassLoader(context); + return result; + } +} diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/api/GlobalApiClient.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/api/GlobalApiClient.java index dea30fb1..9b74296a 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/api/GlobalApiClient.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/api/GlobalApiClient.java @@ -1,13 +1,12 @@ package org.geysermc.floodgate.core.http.api; -import io.micronaut.http.HttpHeaders; -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.Header; -import io.micronaut.http.client.annotation.Client; +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Headers; import org.geysermc.floodgate.core.util.Constants; -@Client("${http.baseUrl.api}") -@Header(name = HttpHeaders.USER_AGENT, value = Constants.USER_AGENT) +@Client +@Headers({"User-Agent: " + Constants.USER_AGENT}) public interface GlobalApiClient { /** * Checks if it can connect to the Global Api, any other status code than 204 or 404 will throw diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/downloads/DownloadClient.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/downloads/DownloadClient.java index 6b70685e..6a6b3013 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/downloads/DownloadClient.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/downloads/DownloadClient.java @@ -1,16 +1,14 @@ package org.geysermc.floodgate.core.http.downloads; -import io.micronaut.http.HttpHeaders; -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.Header; -import io.micronaut.http.client.annotation.Client; -import jakarta.validation.constraints.NotBlank; +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Headers; import java.util.concurrent.CompletableFuture; import org.geysermc.floodgate.core.util.Constants; -@Client("${http.baseUrl.download}") -@Header(name = HttpHeaders.USER_AGENT, value = Constants.USER_AGENT) +@Client +@Headers({"User-Agent: " + Constants.USER_AGENT}) public interface DownloadClient { @Get("/v2/projects/{project}/versions/latest/builds/latest") - CompletableFuture latestBuildFor(@NotBlank String project); + CompletableFuture latestBuildFor(String project); } diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/downloads/LatestBuildResult.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/downloads/LatestBuildResult.java index c0d32b67..1665133d 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/downloads/LatestBuildResult.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/downloads/LatestBuildResult.java @@ -1,7 +1,7 @@ package org.geysermc.floodgate.core.http.downloads; -import io.micronaut.serde.annotation.Serdeable; +import io.avaje.jsonb.Json; -@Serdeable +@Json public record LatestBuildResult(int build) { } diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/link/GlobalLinkClient.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/link/GlobalLinkClient.java index a4287341..cb4e0e4a 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/link/GlobalLinkClient.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/link/GlobalLinkClient.java @@ -25,19 +25,15 @@ package org.geysermc.floodgate.core.http.link; -import static io.micronaut.http.HttpHeaders.USER_AGENT; - -import io.micronaut.core.async.annotation.SingleResult; -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.Header; -import io.micronaut.http.client.annotation.Client; +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Headers; import java.util.concurrent.CompletableFuture; import org.geysermc.floodgate.core.util.Constants; -@Client("${http.baseUrl.api}/v2/link") -@Header(name = USER_AGENT, value = Constants.USER_AGENT) +@Client +@Headers({"User-Agent: " + Constants.USER_AGENT}) public interface GlobalLinkClient { @Get("/bedrock/{xuid}") - @SingleResult CompletableFuture bedrockLink(long xuid); } diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/link/LinkedPlayer.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/link/LinkedPlayer.java index 84b7eeef..63161355 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/link/LinkedPlayer.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/link/LinkedPlayer.java @@ -25,20 +25,20 @@ package org.geysermc.floodgate.core.http.link; -import io.micronaut.serde.annotation.Serdeable; +import io.avaje.jsonb.Json; +import io.avaje.jsonb.Json.Property; import jakarta.annotation.Nullable; -import jakarta.json.bind.annotation.JsonbProperty; import java.util.UUID; import org.geysermc.floodgate.core.util.Utils; -@Serdeable +@Json public record LinkedPlayer( - @JsonbProperty("bedrock_id") + @Property("bedrock_id") @Nullable Long xuid, @Nullable String gamertag, - @JsonbProperty("java_id") + @Property("java_id") @Nullable UUID uuid, - @JsonbProperty("java_name") + @Property("java_name") @Nullable String username ) { public boolean isLinked() { diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/minecraft/MinecraftClient.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/minecraft/MinecraftClient.java index 92ee6379..47099bfe 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/minecraft/MinecraftClient.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/minecraft/MinecraftClient.java @@ -1,17 +1,16 @@ package org.geysermc.floodgate.core.http.minecraft; -import static io.micronaut.http.HttpHeaders.USER_AGENT; - -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.Header; -import io.micronaut.http.client.annotation.Client; +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Headers; import java.util.UUID; import java.util.concurrent.CompletableFuture; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.floodgate.core.util.Constants; -@Client("https://api.minecraftservices.com/minecraft") -@Header(name = USER_AGENT, value = "${http.userAgent}") +@Client +@Headers({"User-Agent: " + Constants.USER_AGENT}) public interface MinecraftClient { @Get("/profile/lookup/name/{name}") CompletableFuture<@Nullable ProfileResult> profileByName(@NonNull String name); diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/minecraft/ProfileResult.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/minecraft/ProfileResult.java index 46110dfd..065d60f9 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/minecraft/ProfileResult.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/minecraft/ProfileResult.java @@ -1,8 +1,8 @@ package org.geysermc.floodgate.core.http.minecraft; -import io.micronaut.serde.annotation.Serdeable; +import io.avaje.jsonb.Json; import org.checkerframework.checker.nullness.qual.NonNull; -@Serdeable +@Json public record ProfileResult(@NonNull String id, @NonNull String name) { } diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/ProfileProperty.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/ProfileProperty.java index 5881297b..a23e9d2e 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/ProfileProperty.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/ProfileProperty.java @@ -1,9 +1,9 @@ package org.geysermc.floodgate.core.http.mojang; -import io.micronaut.serde.annotation.Serdeable; -import jakarta.validation.constraints.NotNull; +import io.avaje.jsonb.Json; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -@Serdeable -public record ProfileProperty(@NotNull String name, @NotNull String value, @Nullable String signature) { +@Json +public record ProfileProperty(@NonNull String name, @NonNull String value, @Nullable String signature) { } diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/ProfileWithProperties.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/ProfileWithProperties.java index 588678bc..520746c0 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/ProfileWithProperties.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/ProfileWithProperties.java @@ -1,10 +1,10 @@ package org.geysermc.floodgate.core.http.mojang; -import io.micronaut.serde.annotation.Serdeable; +import io.avaje.jsonb.Json; import java.util.List; import org.checkerframework.checker.nullness.qual.Nullable; -@Serdeable +@Json public record ProfileWithProperties(String id, String name, List properties) { public @Nullable ProfileProperty texture() { for (ProfileProperty property : properties) { diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/SessionServerClient.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/SessionServerClient.java index 623f963d..f89aa7e9 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/SessionServerClient.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/mojang/SessionServerClient.java @@ -1,17 +1,16 @@ package org.geysermc.floodgate.core.http.mojang; -import static io.micronaut.http.HttpHeaders.USER_AGENT; - -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.Header; -import io.micronaut.http.client.annotation.Client; -import jakarta.validation.constraints.NotNull; +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Headers; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import org.geysermc.floodgate.core.util.Constants; +import org.jetbrains.annotations.NotNull; -@Client("https://sessionserver.mojang.com/session/minecraft") -@Header(name = USER_AGENT, value = "${http.userAgent}") +@Client +@Headers({"User-Agent: " + Constants.USER_AGENT}) public interface SessionServerClient { - @Get("/profile/{uuid}?unsigned=false") + @Get("/profile/{uuid}/?unsigned=false") CompletableFuture profileWithProperties(@NotNull UUID uuid); } diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/GetGamertagResult.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/GetGamertagResult.java index 32bdff1f..041ac335 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/GetGamertagResult.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/GetGamertagResult.java @@ -25,9 +25,9 @@ package org.geysermc.floodgate.core.http.xbox; -import io.micronaut.serde.annotation.Serdeable; +import io.avaje.jsonb.Json; import jakarta.annotation.Nullable; -@Serdeable +@Json public record GetGamertagResult(@Nullable String gamertag) { } diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/GetXuidResult.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/GetXuidResult.java index 7c321592..1aadce6a 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/GetXuidResult.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/GetXuidResult.java @@ -25,9 +25,9 @@ package org.geysermc.floodgate.core.http.xbox; -import io.micronaut.serde.annotation.Serdeable; +import io.avaje.jsonb.Json; import jakarta.annotation.Nullable; -@Serdeable +@Json public record GetXuidResult(@Nullable Long xuid) { } diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/XboxClient.java b/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/XboxClient.java index c4cbb8c0..4428c496 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/XboxClient.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/http/xbox/XboxClient.java @@ -25,21 +25,21 @@ package org.geysermc.floodgate.core.http.xbox; -import io.micronaut.http.HttpHeaders; -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.Header; -import io.micronaut.http.client.annotation.Client; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Headers; import java.util.concurrent.CompletableFuture; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.floodgate.core.util.Constants; +import org.jetbrains.annotations.Range; -@Client("${http.baseUrl.api}/v2/xbox") -@Header(name = HttpHeaders.USER_AGENT, value = Constants.USER_AGENT) +@Client +@Headers({"User-Agent: " + Constants.USER_AGENT}) public interface XboxClient { @Get("/xuid/{gamertag}") CompletableFuture xuidByGamertag( - @NotNull @Size(min = 1, max = 16) String gamertag + @Range(from = 1, to = 16) + @NonNull String gamertag ); @Get("/gamertag/{xuid}") diff --git a/core/common/src/main/java/org/geysermc/floodgate/core/link/LocalPlayerLinking.java b/core/common/src/main/java/org/geysermc/floodgate/core/link/LocalPlayerLinking.java index ccdf2e3b..a8cf5493 100644 --- a/core/common/src/main/java/org/geysermc/floodgate/core/link/LocalPlayerLinking.java +++ b/core/common/src/main/java/org/geysermc/floodgate/core/link/LocalPlayerLinking.java @@ -27,13 +27,11 @@ package org.geysermc.floodgate.core.link; import io.micronaut.context.annotation.Replaces; import io.micronaut.context.annotation.Requires; -import io.micronaut.scheduling.TaskExecutors; import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.inject.Singleton; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.floodgate.core.database.PendingLinkRepository; import org.geysermc.floodgate.core.database.PlayerLinkRepository; @@ -50,10 +48,6 @@ public class LocalPlayerLinking extends CommonPlayerLink { @Inject PlayerLinkRepository linkRepository; @Inject PendingLinkRepository pendingLinkRepository; - @Inject - @Named(TaskExecutors.IO) - ExecutorService executor; - @Override public CompletableFuture addLink( @NonNull UUID javaUniqueId, diff --git a/gradle.properties b/gradle.properties index 06db680e..b08de121 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,4 +3,4 @@ org.gradle.caching=true org.gradle.parallel=true version=3.0.0-SNAPSHOT -micronautVersion=4.3.1 \ No newline at end of file +micronautVersion=4.6.0 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d5417270..463edc3c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] # parent -micronaut-gradle = "4.3.2" +micronaut-gradle = "4.4.2" lombok = "8.4" # api @@ -19,6 +19,8 @@ snakeyaml = "2.0" bstats = "3.0.3" adventure = "4.16.0" adventure-platform = "4.3.2" +avaje-http = "2.7" +avaje-jsonb = "2.1" # bungee bungee = "1.20-R0.3-SNAPSHOT" @@ -70,13 +72,12 @@ adventure-platform-bungee = { module = "net.kyori:adventure-platform-bungeecord" micronaut-inject = { module = "io.micronaut:micronaut-inject" } micronaut-inject-java = { module = "io.micronaut:micronaut-inject-java" } micronaut-context = { module = "io.micronaut:micronaut-context" } -micronaut-http-client = { module = "io.micronaut:micronaut-http-client-jdk" } -micronaut-validation = { module = "io.micronaut.validation:micronaut-validation" } -micronaut-validation-processor = { module = "io.micronaut.validation:micronaut-validation-processor" } -micronaut-serde-jsonp = { module = "io.micronaut.serde:micronaut-serde-jsonp" } -micronaut-serde-processor = { module = "io.micronaut.serde:micronaut-serde-processor" } -jakarta-jsonb = { module = "jakarta.json.bind:jakarta.json.bind-api" } +avaje-http-client = { module = "io.avaje:avaje-http-client", version.ref = "avaje-http" } +avaje-http-api = { module = "io.avaje:avaje-http-api", version.ref = "avaje-http" } +avaje-http-client-generator = { module = "io.avaje:avaje-http-client-generator", version.ref = "avaje-http" } +avaje-jsonb = { module = "io.avaje:avaje-jsonb", version.ref = "avaje-jsonb" } +avaje-jsonb-generator = { module = "io.avaje:avaje-jsonb-generator", version.ref = "avaje-jsonb" } netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" }