summaryrefslogtreecommitdiff
path: root/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/CodeWithMeGuestLauncher.kt
blob: b2d2ca3298c9344d527b154625b9d21452ceeabb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package com.intellij.remoteDev.downloader

import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.Task.Backgroundable
import com.intellij.openapi.project.Project
import com.intellij.openapi.rd.createLifetime
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.NlsContexts
import com.intellij.remoteDev.RemoteDevUtilBundle
import com.intellij.remoteDev.util.UrlUtil
import com.intellij.util.application
import com.intellij.util.fragmentParameters
import com.jetbrains.rd.util.lifetime.Lifetime
import org.jetbrains.annotations.ApiStatus
import java.nio.file.Path
import java.util.concurrent.ConcurrentHashMap

@ApiStatus.Experimental
object CodeWithMeGuestLauncher {
  private val LOG = logger<CodeWithMeGuestLauncher>()

  private val alreadyDownloading = ConcurrentHashMap.newKeySet<String>()

  fun downloadCompatibleClientAndLaunch(project: Project?, url: String, @NlsContexts.DialogTitle product: String, onDone: (Lifetime) -> Unit) {
    if (!application.isDispatchThread) {
      // starting a task from background will call invokeLater, but with wrong modality, so do it ourselves
      application.invokeLater({ downloadCompatibleClientAndLaunch(project, url, product, onDone) }, ModalityState.any())
      return
    }

    val uri = UrlUtil.parseOrShowError(url, product) ?: return

    if (!alreadyDownloading.add(url)) {
      LOG.info("Already downloading a client for $url")
      return
    }

    ProgressManager.getInstance().run(object : Backgroundable(project, RemoteDevUtilBundle.message("launcher.title"), true) {

      private var clientLifetime : Lifetime = Lifetime.Terminated

      override fun run(progressIndicator: ProgressIndicator) {
        try {
          val sessionInfo = when (uri.scheme) {
            "tcp", "gwws" -> {
              val clientBuild = uri.fragmentParameters["cb"] ?: error("there is no client build in url")
              val jreBuild = uri.fragmentParameters["jb"] ?: error("there is no jre build in url")
              val unattendedMode = uri.fragmentParameters["jt"] != null

              CodeWithMeClientDownloader.createSessionInfo(clientBuild, jreBuild, unattendedMode)
            }
            "http", "https" -> {
              progressIndicator.text = RemoteDevUtilBundle.message("launcher.get.client.info")
              ThinClientSessionInfoFetcher.getSessionUrl(uri)
            }
            else -> {
              error("scheme '${uri.scheme} is not supported'")
            }
          }

          val pair = CodeWithMeClientDownloader.downloadClientAndJdk(sessionInfo, progressIndicator)
          if (pair == null) return

          clientLifetime = runDownloadedClient(
            lifetime = project?.createLifetime() ?: Lifetime.Eternal,
            pathToClient = pair.first,
            pathToJre = pair.second,
            urlForThinClient = url,
            product = product,
            progressIndicator = progressIndicator
          )
        }
        catch (t: Throwable) {
          LOG.warn(t)
          application.invokeLater({
            Messages.showErrorDialog(
              RemoteDevUtilBundle.message("error.url.issue", t.message ?: "Unknown"),
              product)
          }, ModalityState.any())
        }
        finally {
          alreadyDownloading.remove(url)
        }
      }

      override fun onSuccess() = onDone.invoke(clientLifetime)
      override fun onCancel() = Unit
    })
  }

  fun runDownloadedClient(lifetime: Lifetime, pathToClient: Path, pathToJre: Path, urlForThinClient: String,
                          @NlsContexts.DialogTitle product: String, progressIndicator: ProgressIndicator?): Lifetime {
    // todo: offer to connect as-is?
    try {
      progressIndicator?.text = RemoteDevUtilBundle.message("launcher.launch.client")
      progressIndicator?.text2 = pathToClient.toString()
      val thinClientLifetime = CodeWithMeClientDownloader.runCwmGuestProcessFromDownload(lifetime, urlForThinClient, pathToClient, pathToJre)

      // Wait a bit until process will be launched and only after that finish task
      Thread.sleep(3000)

      return thinClientLifetime
    }
    catch (t: Throwable) {
      Logger.getInstance(javaClass).warn(t)
      application.invokeLater {
        Messages.showErrorDialog(
          RemoteDevUtilBundle.message("error.guest.run.issue", t.message ?: "Unknown"),
          product)
      }
      return Lifetime.Terminated
    }
  }
}