diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 5a6db27e..7d2eb0d8 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -28,7 +28,7 @@ jobs: - name: Setup java uses: actions/setup-java@v4 with: - java-version: 21 + java-version: 22 check-latest: true distribution: 'zulu' diff --git a/cloudnet-rest-module/build.gradle.kts b/cloudnet-rest-module/build.gradle.kts index 11f74c99..818c2993 100644 --- a/cloudnet-rest-module/build.gradle.kts +++ b/cloudnet-rest-module/build.gradle.kts @@ -21,6 +21,10 @@ plugins { alias(libs.plugins.juppiter) } +repositories { + maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") +} + dependencies { api(projects.webApi) moduleLibrary(libs.guava) @@ -49,8 +53,8 @@ dependencies { compileOnly(libs.logbackCore) compileOnly(libs.logbackClassic) - compileOnly("eu.cloudnetservice.cloudnet:node:4.0.0-RC10") - compileOnly("eu.cloudnetservice.cloudnet:bridge:4.0.0-RC10") + compileOnly("eu.cloudnetservice.cloudnet:node:4.0.0-RC11-SNAPSHOT") + compileOnly("eu.cloudnetservice.cloudnet:bridge:4.0.0-RC11-SNAPSHOT") } tasks.withType { @@ -65,6 +69,11 @@ tasks.withType { } } +tasks.withType { + sourceCompatibility = JavaVersion.VERSION_22.toString() + targetCompatibility = JavaVersion.VERSION_22.toString() +} + moduleJson { main = "eu.cloudnetservice.ext.modules.rest.CloudNetRestModule" name = "CloudNet-Rest" diff --git a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/CloudNetRestModule.java b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/CloudNetRestModule.java index 798b5ea0..fd9b5688 100644 --- a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/CloudNetRestModule.java +++ b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/CloudNetRestModule.java @@ -16,12 +16,14 @@ package eu.cloudnetservice.ext.modules.rest; +import dev.derklaro.aerogel.SpecifiedInjector; import dev.derklaro.aerogel.binding.BindingBuilder; import eu.cloudnetservice.common.language.I18n; import eu.cloudnetservice.driver.document.DocumentFactory; import eu.cloudnetservice.driver.event.EventManager; import eu.cloudnetservice.driver.inject.InjectionLayer; import eu.cloudnetservice.driver.module.ModuleLifeCycle; +import eu.cloudnetservice.driver.module.ModuleProvider; import eu.cloudnetservice.driver.module.ModuleTask; import eu.cloudnetservice.driver.module.driver.DriverModule; import eu.cloudnetservice.ext.modules.rest.config.RestConfiguration; @@ -46,7 +48,9 @@ import eu.cloudnetservice.ext.rest.api.auth.RestUserManagementLoader; import eu.cloudnetservice.ext.rest.api.factory.HttpComponentFactoryLoader; import eu.cloudnetservice.ext.rest.validation.ValidationHandlerMethodContextDecorator; +import eu.cloudnetservice.node.TickLoop; import eu.cloudnetservice.node.command.CommandProvider; +import jakarta.inject.Named; import jakarta.inject.Singleton; import lombok.NonNull; import org.slf4j.Logger; @@ -63,7 +67,7 @@ public void loadLanguageFile() { } @ModuleTask(order = 127, lifecycle = ModuleLifeCycle.STARTED) - public void initHttpServer(@NonNull InjectionLayer injectionLayer) { + public void initHttpServer(@Named("module") @NonNull InjectionLayer injectionLayer) { var restConfig = this.readConfig(RestConfiguration.class, () -> RestConfiguration.DEFAULT, DocumentFactory.json()); // construct the http server component @@ -124,16 +128,30 @@ public void registerRestCommand(@NonNull CommandProvider commandProvider) { commandProvider.register(RestCommand.class); } + @ModuleTask(lifecycle = ModuleLifeCycle.STARTED) + public void scheduleBridgeInitialization( + @NonNull TickLoop tickLoop, + @NonNull ModuleProvider moduleProvider, + @NonNull HttpServer server, + @NonNull @Named("module") InjectionLayer moduleLayer + ) { + // we want to register the bridge handlers after all modules are started + tickLoop.runTask(() -> CloudNetBridgeInitializer.installBridgeHandler(moduleProvider, server, moduleLayer)); + } + @ModuleTask(lifecycle = ModuleLifeCycle.STARTED) public void registerListener(@NonNull EventManager eventManager) { eventManager.registerListener(RestUserUpdateListener.class); - eventManager.registerListener(CloudNetBridgeInitializer.class); } @ModuleTask(lifecycle = ModuleLifeCycle.STOPPED) - public void unregisterModule(@NonNull HttpServer httpServer) { + public void unregisterModule( + @NonNull HttpServer httpServer, + @Named("module") InjectionLayer layer + ) { try { httpServer.close(); + layer.injector().removeConstructedBindings(); } catch (Exception exception) { LOGGER.error("Unable to close http server while disabling cloudnet rest module.", exception); } diff --git a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/dto/JsonConfigurationDto.java b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/dto/JsonConfigurationDto.java index d302613f..41a9ed47 100644 --- a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/dto/JsonConfigurationDto.java +++ b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/dto/JsonConfigurationDto.java @@ -22,7 +22,6 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; -import java.util.Collection; import java.util.Map; import java.util.Set; import lombok.NonNull; @@ -65,19 +64,12 @@ public final class JsonConfigurationDto implements Dto { @NotNull private final Map ipAliases; - @Valid - @NotNull - private final Collection httpListeners; - @Valid @NotNull private final SSLConfigurationDto clientSslConfig; @Valid @NotNull private final SSLConfigurationDto serverSslConfig; - @Valid - @NotNull - private final SSLConfigurationDto webSslConfig; @NotNull private final Document properties; @@ -97,10 +89,8 @@ public JsonConfigurationDto( String jvmCommand, String hostAddress, Map ipAliases, - Collection httpListeners, SSLConfigurationDto clientSslConfig, SSLConfigurationDto serverSslConfig, - SSLConfigurationDto webSslConfig, Document properties ) { this.language = language; @@ -117,10 +107,8 @@ public JsonConfigurationDto( this.jvmCommand = jvmCommand; this.hostAddress = hostAddress; this.ipAliases = ipAliases; - this.httpListeners = httpListeners; this.clientSslConfig = clientSslConfig; this.serverSslConfig = serverSslConfig; - this.webSslConfig = webSslConfig; this.properties = properties; } @@ -141,10 +129,8 @@ public JsonConfigurationDto( config.javaCommand(this.jvmCommand); config.hostAddress(this.hostAddress); config.ipAliases(this.ipAliases); - config.httpListeners(Dto.toList(this.httpListeners)); config.clientSSLConfig(this.clientSslConfig.toEntity()); config.serverSSLConfig(this.serverSslConfig.toEntity()); - config.webSSLConfig(this.webSslConfig.toEntity()); config.properties(this.properties); return config; } diff --git a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/dto/SSLConfigurationDto.java b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/dto/SSLConfigurationDto.java index d474c711..bb053f54 100644 --- a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/dto/SSLConfigurationDto.java +++ b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/dto/SSLConfigurationDto.java @@ -31,19 +31,22 @@ public final class SSLConfigurationDto implements Dto { private final Path certificatePath; @NotNull private final Path privateKeyPath; + private final String privateKeyPassword; public SSLConfigurationDto( boolean enabled, boolean clientAuth, Path trustCertificatePath, Path certificatePath, - Path privateKeyPath + Path privateKeyPath, + String privateKeyPassword ) { this.enabled = enabled; this.clientAuth = clientAuth; this.trustCertificatePath = trustCertificatePath; this.certificatePath = certificatePath; this.privateKeyPath = privateKeyPath; + this.privateKeyPassword = privateKeyPassword; } @Override @@ -53,6 +56,7 @@ public SSLConfigurationDto( this.clientAuth, this.trustCertificatePath, this.certificatePath, - this.privateKeyPath); + this.privateKeyPath, + this.privateKeyPassword); } } diff --git a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/listener/CloudNetBridgeInitializer.java b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/listener/CloudNetBridgeInitializer.java index 9ce69c6a..b0f3d8bc 100644 --- a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/listener/CloudNetBridgeInitializer.java +++ b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/listener/CloudNetBridgeInitializer.java @@ -16,30 +16,26 @@ package eu.cloudnetservice.ext.modules.rest.listener; -import eu.cloudnetservice.driver.event.EventListener; +import dev.derklaro.aerogel.SpecifiedInjector; import eu.cloudnetservice.driver.inject.InjectionLayer; import eu.cloudnetservice.driver.module.ModuleLifeCycle; import eu.cloudnetservice.driver.module.ModuleProvider; import eu.cloudnetservice.ext.modules.rest.v3.bridge.V3HttpHandlerPlayer; import eu.cloudnetservice.ext.rest.api.HttpServer; -import eu.cloudnetservice.node.event.CloudNetNodePostInitializationEvent; -import jakarta.inject.Singleton; import lombok.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@Singleton public class CloudNetBridgeInitializer { private static final String BRIDGE_MODULE_NAME = "CloudNet-Bridge"; private static final Logger LOGGER = LoggerFactory.getLogger(CloudNetBridgeInitializer.class); - @EventListener - public void handleNodePostInit( - @NonNull CloudNetNodePostInitializationEvent event, + public static void installBridgeHandler( @NonNull ModuleProvider moduleProvider, - @NonNull HttpServer server + @NonNull HttpServer server, + @NonNull InjectionLayer moduleLayer ) { var bridgeModule = moduleProvider.module(BRIDGE_MODULE_NAME); if (bridgeModule == null || bridgeModule.moduleLifeCycle() != ModuleLifeCycle.STARTED) { @@ -47,8 +43,7 @@ public void handleNodePostInit( return; } - var layer = InjectionLayer.findLayerOf(this.getClass()); - server.annotationParser().parseAndRegister(layer.instance(V3HttpHandlerPlayer.class)); + server.annotationParser().parseAndRegister(moduleLayer.instance(V3HttpHandlerPlayer.class)); LOGGER.debug("Successfully registered V3HttpHandlerPlayer as {} is present", BRIDGE_MODULE_NAME); } } diff --git a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/v3/V3HttpHandlerModule.java b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/v3/V3HttpHandlerModule.java index 556c1120..779cb918 100644 --- a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/v3/V3HttpHandlerModule.java +++ b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/v3/V3HttpHandlerModule.java @@ -229,7 +229,7 @@ public V3HttpHandlerModule(@NonNull ModulesHolder modulesHolder, @NonNull Module // download the module var target = this.moduleProvider.moduleDirectoryPath().resolve(entry.name() + ".jar"); Unirest.get(entry.url()) - .connectTimeout(5000) + .requestTimeout(5000) .thenConsume(rawResponse -> FileUtil.copy(rawResponse.getContent(), target)); // validate the downloaded file diff --git a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/v3/V3HttpHandlerNode.java b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/v3/V3HttpHandlerNode.java index 77ed5100..371b486e 100644 --- a/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/v3/V3HttpHandlerNode.java +++ b/cloudnet-rest-module/src/main/java/eu/cloudnetservice/ext/modules/rest/v3/V3HttpHandlerNode.java @@ -19,6 +19,7 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.ConsoleAppender; import ch.qos.logback.core.OutputStreamAppender; +import eu.cloudnetservice.common.concurrent.TaskUtil; import eu.cloudnetservice.common.util.StringUtil; import eu.cloudnetservice.driver.CloudNetVersion; import eu.cloudnetservice.driver.module.ModuleProvider; @@ -228,7 +229,7 @@ public void handle(@NonNull WebSocketChannel channel, @NonNull WebSocketFrameTyp if (this.user.hasScope("cloudnet_rest:node_send_commands")) { var commandLine = new String(bytes, StandardCharsets.UTF_8); var commandSource = new DriverCommandSource(); - V3HttpHandlerNode.this.commandProvider.execute(commandSource, commandLine).getOrNull(); + TaskUtil.getOrDefault(V3HttpHandlerNode.this.commandProvider.execute(commandSource, commandLine), null); for (var message : commandSource.messages()) { this.channel.sendWebSocketFrame(WebSocketFrameType.TEXT, message);