From d941f69f01171239ad18944632c089ae10b61a81 Mon Sep 17 00:00:00 2001 From: mustard Date: Fri, 11 Oct 2024 01:51:27 +0800 Subject: [PATCH 1/4] [JetBrains] improve handling of client connection termination --- .../io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt b/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt index f85f4ba905b315..75cd9b78819b2d 100644 --- a/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt +++ b/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt @@ -297,7 +297,11 @@ class GitpodConnectionProvider : GatewayConnectionProvider { ) clientHandle.clientClosed.advise(connectionLifetime) { application.invokeLater { - connectionLifetime.terminate() + GlobalScope.launch { + // Delay for 5 seconds to wait the thinClient actually close + delay(5000) + connectionLifetime.terminate() + } } } clientHandle.onClientPresenceChanged.advise(connectionLifetime) { From 6c1488e8920b9a5dafa3bb729a0046eb14eb0216 Mon Sep 17 00:00:00 2001 From: mustard Date: Fri, 11 Oct 2024 22:11:48 +0800 Subject: [PATCH 2/4] Hold connection provider until workspace stopped --- .../gateway/GitpodConnectionProvider.kt | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt b/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt index 75cd9b78819b2d..2b62d3d8d549de 100644 --- a/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt +++ b/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt @@ -46,6 +46,7 @@ import io.gitpod.jetbrains.gateway.common.GitpodConnectionHandleFactory import io.gitpod.jetbrains.icons.GitpodIcons import kotlinx.coroutines.* import kotlinx.coroutines.future.await +import java.awt.Component import java.net.URL import java.net.http.HttpClient import java.net.http.HttpRequest @@ -71,6 +72,17 @@ class GitpodConnectionProvider : GatewayConnectionProvider { private val jacksonMapper = jacksonObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + + private fun showTimedOutDialogDialog(workspaceId: String, detail: String?) { + val title = "Workspace Timed Out" + val message = "Your workspace $workspaceId has timed out${if (detail.isNullOrBlank()) "" else " : $detail"}." + val okButton = Messages.getOkButton() + val options = arrayOf(okButton) + val defaultIndex = 0 + val icon = Messages.getInformationIcon() + Messages.showDialog(message, title, options, defaultIndex, icon) + } + override suspend fun connect( parameters: Map, requestor: ConnectionRequestor @@ -188,6 +200,7 @@ class GitpodConnectionProvider : GatewayConnectionProvider { var thinClientJob: Job? = null var lastUpdate: WorkspaceInstance? = null + var canceledByGitpod = false try { for (update in updates) { try { @@ -255,8 +268,8 @@ class GitpodConnectionProvider : GatewayConnectionProvider { statusMessage.text = "" } } - if (update.status.phase == "stopping" || update.status.phase == "stopped") { + canceledByGitpod = true thinClientJob?.cancel() thinClient?.close() } @@ -295,11 +308,34 @@ class GitpodConnectionProvider : GatewayConnectionProvider { SshHostTunnelConnector(credentials), URI(joinLinkResp.joinLink) ) + var triggeredClientClosed = false clientHandle.clientClosed.advise(connectionLifetime) { - application.invokeLater { - GlobalScope.launch { - // Delay for 5 seconds to wait the thinClient actually close - delay(5000) + // Been canceled by user + if (!canceledByGitpod) { + application.invokeLater { + connectionLifetime.terminate() + } + return@advise + } + if (triggeredClientClosed) { + return@advise + } + triggeredClientClosed = true + // Wait until workspace is stopped + suspend fun waitUntilStopped(): Boolean { + while (lastUpdate.status.phase != "stopped") { + delay(1000) + } + return true + } + // Check if it's timed out, if so, show timed out dialog + GlobalScope.launch { + val isInStoppedPhase = waitUntilStopped() + val isTimedOut = isInStoppedPhase && phaseMessage.text == "Timed Out" + application.invokeLater { + if (isTimedOut) { + showTimedOutDialogDialog(connectParams.resolvedWorkspaceId, lastUpdate.status.conditions.timeout) + } connectionLifetime.terminate() } } From 8a3047a50069ea40bab87f46367a7be550c378b7 Mon Sep 17 00:00:00 2001 From: mustard Date: Fri, 11 Oct 2024 22:13:48 +0800 Subject: [PATCH 3/4] use lifetime launch --- .../io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt b/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt index 2b62d3d8d549de..588ff500f61a7f 100644 --- a/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt +++ b/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt @@ -41,6 +41,7 @@ import com.jetbrains.rd.util.ConcurrentHashMap import com.jetbrains.rd.util.URI import com.jetbrains.rd.util.lifetime.Lifetime import com.jetbrains.rd.util.lifetime.LifetimeDefinition +import com.jetbrains.rd.util.threading.coroutines.launch import io.gitpod.gitpodprotocol.api.entities.WorkspaceInstance import io.gitpod.jetbrains.gateway.common.GitpodConnectionHandleFactory import io.gitpod.jetbrains.icons.GitpodIcons @@ -329,7 +330,7 @@ class GitpodConnectionProvider : GatewayConnectionProvider { return true } // Check if it's timed out, if so, show timed out dialog - GlobalScope.launch { + connectionLifetime.launch { val isInStoppedPhase = waitUntilStopped() val isTimedOut = isInStoppedPhase && phaseMessage.text == "Timed Out" application.invokeLater { From 6cf5a2f16e020cbb27e7f18d0da4471d13f524be Mon Sep 17 00:00:00 2001 From: mustard Date: Fri, 11 Oct 2024 22:43:14 +0800 Subject: [PATCH 4/4] delay 5 seconds --- .../gitpod/jetbrains/gateway/GitpodConnectionProvider.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt b/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt index 588ff500f61a7f..b4c5fa5b688586 100644 --- a/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt +++ b/components/ide/jetbrains/gateway-plugin/src/main/kotlin/io/gitpod/jetbrains/gateway/GitpodConnectionProvider.kt @@ -313,8 +313,13 @@ class GitpodConnectionProvider : GatewayConnectionProvider { clientHandle.clientClosed.advise(connectionLifetime) { // Been canceled by user if (!canceledByGitpod) { - application.invokeLater { - connectionLifetime.terminate() + connectionLifetime.launch { + // Delay for 5 seconds to see if thinClient could be terminated in time + // Then we don't see error dialog from Gateway + delay(5000) + application.invokeLater { + connectionLifetime.terminate() + } } return@advise }