From 0a394b8fa42da4873092723218df857af0e881ed Mon Sep 17 00:00:00 2001 From: "Alexander.Glukhov" Date: Mon, 17 Nov 2025 12:13:35 +0100 Subject: [PATCH] [gradle][eel][IDEA-382138] bind the ToolingProxy's proxy port lifecycle to the external ExternalSystemTask GitOrigin-RevId: 160ff3414b3122f8c344931cac388570e16b6fd2 --- .../execution/target/GradleServerRunner.kt | 3 ++- .../execution/target/ToolingProxyConnector.kt | 3 +++ .../GradleServerConfigurationProvider.kt | 15 +++++++++-- ...lTargetEnvironmentConfigurationProvider.kt | 15 ++++++++--- .../execution/eel/GradleEelProxyManager.kt | 26 +++++++++++++++++++ 5 files changed, 55 insertions(+), 7 deletions(-) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/target/GradleServerRunner.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/target/GradleServerRunner.kt index d6c27664adff..fbc9cb8cb0aa 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/target/GradleServerRunner.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/target/GradleServerRunner.kt @@ -63,7 +63,8 @@ internal class GradleServerRunner(private val connection: TargetProjectConnectio val connectorFactory = ToolingProxyConnector.ToolingProxyConnectorFactory( classloaderHolder, serverEnvironmentSetup, - connection.environmentConfigurationProvider as? GradleServerConfigurationProvider + connection.environmentConfigurationProvider as? GradleServerConfigurationProvider, + connection.taskId ) val serverProcessListener = GradleServerProcessListener( targetProgressIndicator, diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/target/ToolingProxyConnector.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/target/ToolingProxyConnector.kt index 0ad5340148fd..332a1bf68c5b 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/target/ToolingProxyConnector.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/target/ToolingProxyConnector.kt @@ -3,6 +3,7 @@ package org.jetbrains.plugins.gradle.execution.target import com.intellij.execution.target.HostPort import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId import com.intellij.openapi.externalSystem.service.remote.MultiLoaderObjectInputStream import com.intellij.openapi.externalSystem.util.wsl.connectRetrying import com.intellij.openapi.progress.ProgressManager @@ -139,6 +140,7 @@ internal class ToolingProxyConnector( private val classloaderHolder: GradleToolingProxyClassloaderHolder, private val serverEnvironmentSetup: GradleServerEnvironmentSetup, private val configurationProvider: GradleServerConfigurationProvider?, + private val taskId: ExternalSystemTaskId?, ) { fun getConnector(host: String, port: Int): ToolingProxyConnector { @@ -157,6 +159,7 @@ internal class ToolingProxyConnector( HostPort(host, port) } val communicationAddress = configurationProvider?.getClientCommunicationAddress( + taskId, serverEnvironmentSetup.getEnvironmentConfiguration(), hostPort ) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleServerConfigurationProvider.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleServerConfigurationProvider.kt index 255fb18ff614..c7bb48c984b7 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleServerConfigurationProvider.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleServerConfigurationProvider.kt @@ -3,12 +3,23 @@ package org.jetbrains.plugins.gradle.service.execution import com.intellij.execution.target.HostPort import com.intellij.execution.target.TargetEnvironmentConfiguration +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId import com.intellij.openapi.externalSystem.service.execution.TargetEnvironmentConfigurationProvider import org.jetbrains.annotations.ApiStatus @ApiStatus.Experimental interface GradleServerConfigurationProvider : TargetEnvironmentConfigurationProvider { + fun getServerBindingAddress(targetEnvironmentConfiguration: TargetEnvironmentConfiguration): HostPort? = null - fun getClientCommunicationAddress(targetEnvironmentConfiguration: TargetEnvironmentConfiguration, - gradleServerHostPort: HostPort): HostPort? = null + + fun getClientCommunicationAddress( + targetEnvironmentConfiguration: TargetEnvironmentConfiguration, + gradleServerHostPort: HostPort, + ): HostPort? = getClientCommunicationAddress(null, targetEnvironmentConfiguration, gradleServerHostPort) + + fun getClientCommunicationAddress( + taskId: ExternalSystemTaskId?, + targetEnvironmentConfiguration: TargetEnvironmentConfiguration, + gradleServerHostPort: HostPort, + ): HostPort? = null } \ No newline at end of file diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/eel/EelTargetEnvironmentConfigurationProvider.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/eel/EelTargetEnvironmentConfigurationProvider.kt index a04b23c5ea3a..ae429dd8fbed 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/eel/EelTargetEnvironmentConfigurationProvider.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/eel/EelTargetEnvironmentConfigurationProvider.kt @@ -1,9 +1,10 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.gradle.service.execution.eel +import com.intellij.execution.target.EelTargetEnvironmentRequest import com.intellij.execution.target.HostPort import com.intellij.execution.target.TargetEnvironmentConfiguration -import com.intellij.execution.target.EelTargetEnvironmentRequest +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId import com.intellij.openapi.progress.runBlockingCancellable import com.intellij.openapi.project.Project import com.intellij.openapi.util.io.toCanonicalPath @@ -63,13 +64,19 @@ class EelTargetEnvironmentConfigurationProvider(val eel: EelApi, val project: Pr } override fun getClientCommunicationAddress( + taskId: ExternalSystemTaskId?, targetEnvironmentConfiguration: TargetEnvironmentConfiguration, gradleServerHostPort: HostPort, ): HostPort { - return runBlockingCancellable { - val forwardedPort = forwardToolingProxyPortOntoLocalMachine(gradleServerHostPort.port) - return@runBlockingCancellable HostPort(NetUtils.getLocalHostString(), forwardedPort) + val localPort = if (taskId == null) { + runBlockingCancellable { + forwardToolingProxyPortOntoLocalMachine(gradleServerHostPort.port) + } } + else { + GradleEelProxyManager.getInstance(project).launchProxy(taskId, gradleServerHostPort.port) + } + return HostPort(NetUtils.getLocalHostString(), localPort) } private suspend fun forwardToolingProxyPortOntoLocalMachine(port: Int): Int { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/eel/GradleEelProxyManager.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/eel/GradleEelProxyManager.kt index 3cbd822b6745..0bbcd990643e 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/eel/GradleEelProxyManager.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/eel/GradleEelProxyManager.kt @@ -10,8 +10,10 @@ import com.intellij.openapi.project.Project import com.intellij.platform.eel.EelDescriptor import com.intellij.platform.eel.EelTunnelsApi import com.intellij.platform.eel.provider.getEelDescriptor +import com.intellij.platform.eel.provider.utils.forwardLocalPort import com.intellij.platform.eel.provider.utils.forwardLocalServer import com.intellij.platform.util.coroutines.childScope +import com.intellij.util.net.NetUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import java.util.concurrent.ConcurrentHashMap @@ -45,6 +47,16 @@ internal class GradleEelProxyManager( fun launchReverseProxy(taskId: ExternalSystemTaskId, localPort: Int): Int = project.getEelDescriptor() .openLocalSink(taskId, localPort) + /** + * Set up a proxy between %localhost%:[@result] and %remote_localhost%:[@remotePort]. + * + * @param taskId the corresponding task ID. + * @param remotePort a remote port that should be forwarded onto the local side. + * @return an opened port on the **local** side. + */ + fun launchProxy(taskId: ExternalSystemTaskId, remotePort: Int): Int = project.getEelDescriptor() + .openRemoteSink(taskId, remotePort) + /** * Get mapping between remote and local port. * @@ -84,6 +96,20 @@ internal class GradleEelProxyManager( } } + private fun EelDescriptor.openRemoteSink(taskId: ExternalSystemTaskId, remotePort: Int): Int { + val remoteHostAddress = EelTunnelsApi.HostAddress.Builder(remotePort.toUShort()) + .connectionTimeout(90.seconds) + .build() + val localPort = NetUtils.findAvailableSocketPort() + return runBlockingCancellable { + val eelApi = toEelApi() + getTaskScope(taskId) + .forwardLocalPort(eelApi.tunnels, localPort, remoteHostAddress) + registerMapping(taskId, localPort, remotePort) + return@runBlockingCancellable localPort + } + } + private fun registerMapping(taskId: ExternalSystemTaskId, localPort: Int, remotePort: Int) { val mapping = mappings.computeIfAbsent(taskId.id) { Mapping() } mapping.add(localPort, remotePort)