[gradle][eel][IDEA-382138] bind the ToolingProxy's proxy port lifecycle to the external ExternalSystemTask

GitOrigin-RevId: 160ff3414b3122f8c344931cac388570e16b6fd2
This commit is contained in:
Alexander.Glukhov
2025-11-17 13:27:23 +00:00
committed by intellij-monorepo-bot
parent db6a582357
commit 0a394b8fa4
5 changed files with 55 additions and 7 deletions
@@ -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,
@@ -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
)
@@ -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
}
@@ -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 {
@@ -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)