PageRenderTime 38ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/org.scala-ide.sdt.debug/src/org/scalaide/debug/internal/launching/SocketListenConnectorScala.scala

http://github.com/scala-ide/scala-ide
Scala | 214 lines | 121 code | 47 blank | 46 comment | 1 complexity | 77124ff24987d488aa46c23d321ad190 MD5 | raw file
Possible License(s): BSD-3-Clause, EPL-1.0
  1. package org.scalaide.debug.internal.launching
  2. import java.io.IOException
  3. import java.util.{ List => JList }
  4. import java.util.{ Map => JMap }
  5. import org.eclipse.core.runtime.IProgressMonitor
  6. import org.eclipse.core.runtime.IStatus
  7. import org.eclipse.core.runtime.Status
  8. import org.eclipse.core.runtime.jobs.Job
  9. import org.eclipse.debug.core.DebugEvent
  10. import org.eclipse.debug.core.DebugPlugin
  11. import org.eclipse.debug.core.ILaunch
  12. import org.eclipse.debug.core.model.IProcess
  13. import org.eclipse.debug.core.model.IStreamsProxy
  14. import org.eclipse.jdi.Bootstrap
  15. import org.eclipse.jdt.launching.IVMConnector
  16. import org.scalaide.debug.internal.ScalaDebugPlugin
  17. import org.scalaide.debug.internal.model.ScalaDebugTarget
  18. import com.sun.jdi.connect.Connector
  19. import com.sun.jdi.connect.ListeningConnector
  20. import com.sun.jdi.connect.TransportTimeoutException
  21. /**
  22. * Listen connector creating a Scala debug session.
  23. * Added to the platform through extension point.
  24. */
  25. class SocketListenConnectorScala extends IVMConnector with SocketConnectorScala {
  26. import SocketConnectorScala._
  27. override def connector(): ListeningConnector = {
  28. import scala.collection.JavaConverters._
  29. Bootstrap.virtualMachineManager().listeningConnectors().asScala.find(_.name() == SocketListenName).getOrElse(
  30. throw ScalaDebugPlugin.wrapInCoreException("Unable to find JDI ListeningConnector", null))
  31. }
  32. // from org.eclipse.jdt.launching.IVMConnector
  33. override val getArgumentOrder: JList[String] = {
  34. import scala.collection.JavaConverters._
  35. List(PortKey).asJava
  36. }
  37. override val getIdentifier: String = ScalaDebugPlugin.id + ".socketListenConnector"
  38. override def getName(): String = "Scala debugger (Socket Listen)"
  39. override def connect(params: JMap[String, String], monitor: IProgressMonitor, launch: ILaunch): Unit = {
  40. val arguments = generateArguments(params)
  41. // the port number is needed for the process label. It should be available if we got that far
  42. import scala.collection.JavaConverters._
  43. val port = arguments.asScala.get("port") match {
  44. case Some(e: Connector.IntegerArgument) =>
  45. e.intValue()
  46. case _ =>
  47. 0
  48. }
  49. // a fake process to display information
  50. val process = ListenForConnectionProcess(launch, port)
  51. // Start a job to wait for VM connections
  52. val job = new ListenForConnectionJob(launch, process, connector(), arguments, extractProjectClasspath(params.asScala.toMap))
  53. job.setPriority(Job.SHORT)
  54. job.schedule()
  55. }
  56. // ------------
  57. }
  58. object ListenForConnectionProcess {
  59. /**
  60. * Create a process instance.
  61. */
  62. def apply(launch: ILaunch, port: Int): ListenForConnectionProcess = {
  63. val process = new ListenForConnectionProcess(launch, port)
  64. launch.addProcess(process)
  65. process.fireEvent(DebugEvent.CREATE)
  66. process
  67. }
  68. }
  69. /**
  70. * A fake process which is displayed while waiting for a VM to connect
  71. */
  72. class ListenForConnectionProcess private (launch: ILaunch, port: Int) extends IProcess {
  73. // -- from org.eclipse.debug.core.model.IProcess
  74. def getExitValue(): Int = {
  75. // should only be used when connection failed
  76. -1
  77. }
  78. def getAttribute(id: String): String = {
  79. // not supported
  80. null
  81. }
  82. def setAttribute(id: String, value: String): Unit = {
  83. // nothing to do
  84. }
  85. def getStreamsProxy(): IStreamsProxy = {
  86. // not supported
  87. null
  88. }
  89. def getLaunch(): ILaunch = launch
  90. def getLabel(): String = label
  91. // -- from org.eclipse.debug.core.model.ITerminate
  92. def terminate(): Unit = {
  93. // not supported
  94. }
  95. override def isTerminated(): Boolean = isTerminatedFlag
  96. def canTerminate(): Boolean = false
  97. // -- from org.eclipse.core.runtime.IAdaptable
  98. override def getAdapter[A](adapter: Class[A]): A = null.asInstanceOf[A]
  99. // ----------
  100. /**
  101. * Called when no VM connected before the timeout.
  102. * Set the flag and the label accordingly.
  103. */
  104. def failed(message: String): Unit = {
  105. label = message
  106. isTerminatedFlag = true
  107. fireEvent(DebugEvent.TERMINATE)
  108. }
  109. /**
  110. * Called when a VM has connected. The fake process is destroyed
  111. */
  112. def done(): Unit = {
  113. launch.removeProcess(this)
  114. }
  115. /**
  116. * Flag holding the terminated state
  117. */
  118. @volatile
  119. private var isTerminatedFlag = false
  120. /**
  121. * Label displayed. Initial value to 'waiting...'. Switched to error message after faiture.
  122. */
  123. @volatile
  124. private var label = "Waiting for connection on port %d...".format(port)
  125. /**
  126. * Utility method to fire debug events.
  127. */
  128. private def fireEvent(kind: Int): Unit = {
  129. DebugPlugin.getDefault().fireDebugEventSet(Array(new DebugEvent(this, kind)))
  130. }
  131. }
  132. /**
  133. * Job waiting for a VM to connect.
  134. * If it is successful, it creates a debug target, otherwise an error message is displayed in the debug view.
  135. */
  136. class ListenForConnectionJob(launch: ILaunch, process: ListenForConnectionProcess, connector: ListeningConnector, arguments: JMap[String, Connector.Argument], projectClasspath: Option[Seq[String]])
  137. extends Job("Scala debugger remote connection listener") {
  138. import SocketConnectorScala._
  139. // -- from org.eclipse.core.runtime.jobs.Job
  140. override def run(monitor: IProgressMonitor): IStatus = {
  141. try {
  142. connector.startListening(arguments)
  143. val virtualMachine = connector.accept(arguments)
  144. connector.stopListening(arguments)
  145. ScalaDebugTarget(virtualMachine, launch, null, true, allowTerminate(launch), projectClasspath)
  146. connectionSuccesful()
  147. Status.OK_STATUS
  148. } catch {
  149. case _: TransportTimeoutException =>
  150. connectionFailed("No connection received before timeout expired")
  151. Status.OK_STATUS
  152. case e: IOException =>
  153. connectionFailed("Problem while waiting to receive connection. See log for more details")
  154. ScalaDebugPlugin.wrapInErrorStatus("Problem while waiting to receive connection", e)
  155. case e: Exception =>
  156. connectionFailed("Unexpected problem while waiting to receive connection. See log for more details")
  157. ScalaDebugPlugin.wrapInErrorStatus("Unexpected problem while waiting to receive connection", e)
  158. }
  159. }
  160. // ------------
  161. def connectionSuccesful(): Unit = {
  162. process.done()
  163. }
  164. def connectionFailed(message: String): Unit = {
  165. process.failed(message)
  166. }
  167. }