1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-26 08:27:12 -07:00

First version of the WebSocket server.

This commit is contained in:
Cedric Beust 2016-06-15 03:31:59 -08:00
parent c52da23354
commit f1e75223a3
4 changed files with 149 additions and 7 deletions

View file

@ -1,8 +1,7 @@
import com.beust.kobalt.TaskResult
import com.beust.kobalt.api.License
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.Scm
import com.beust.kobalt.*
import com.beust.kobalt.api.*
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.homeDir
import com.beust.kobalt.plugin.application.application
@ -137,6 +136,7 @@ val kobaltApp = project(kobaltPluginApi, wrapper) {
"com.google.code.gson:gson:${Versions.gson}",
"com.squareup.retrofit2:retrofit:${Versions.retrofit}",
"com.squareup.retrofit2:converter-gson:${Versions.retrofit}",
"com.squareup.okhttp3:okhttp-ws:${Versions.okhttp}",
"org.codehaus.plexus:plexus-utils:3.0.22",
"biz.aQute.bnd:bndlib:2.4.0",

View file

@ -16,10 +16,15 @@ import com.google.inject.Inject
import java.io.File
import java.nio.file.Paths
interface IProgressListener {
fun onProgress(progress: Int? = null, message: String? = null)
}
class DependencyData @Inject constructor(val executors: KobaltExecutors, val dependencyManager: DependencyManager,
val buildFileCompilerFactory: BuildFileCompiler.IFactory, val pluginInfo: PluginInfo,
val taskManager: TaskManager) {
fun dependenciesDataFor(buildFilePath: String, args: Args) : GetDependenciesData {
fun dependenciesDataFor(buildFilePath: String, args: Args, progressListener: IProgressListener? = null)
: GetDependenciesData {
val projectDatas = arrayListOf<ProjectData>()
fun toDependencyData(d: IClasspathDependency, scope: String): DependencyData {
@ -38,7 +43,10 @@ class DependencyData @Inject constructor(val executors: KobaltExecutors, val dep
}
val allTasks = hashSetOf<TaskData>()
projectResult.projects.forEach { project ->
projectResult.projects.withIndex().forEach { wi ->
val project = wi.value
progressListener?.onProgress(message = "Synchronizing project ${project.name} "
+ (wi.index + 1) + "/" + projectResult.projects.size)
val compileDependencies = pluginDependencies.map { toDependencyData(it, "compile") } +
allDeps(project.compileDependencies).map { toDependencyData(it, "compile") } +
allDeps(project.compileProvidedDependencies).map { toDependencyData(it, "compile") }
@ -88,5 +96,9 @@ class DependencyData @Inject constructor(val executors: KobaltExecutors, val dep
class GetDependenciesData(val projects: List<ProjectData> = emptyList(),
val allTasks: Collection<TaskData> = emptySet(),
val errorMessage: String?)
val errorMessage: String?) {
companion object {
val NAME = "GetDependencies"
}
}
}

View file

@ -1,6 +1,7 @@
package com.beust.kobalt.app.remote
import com.beust.kobalt.Args
import com.beust.kobalt.KobaltException
import com.beust.kobalt.SystemProperties
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.app.MainModule
@ -9,11 +10,19 @@ import com.beust.kobalt.internal.KobaltSettings
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.log
import com.beust.kobalt.misc.warn
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.google.inject.Guice
import com.google.inject.Inject
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody
import okhttp3.ws.WebSocket
import okhttp3.ws.WebSocketCall
import okhttp3.ws.WebSocketListener
import okio.Buffer
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
@ -28,7 +37,7 @@ import java.util.concurrent.Executors
fun main(argv: Array<String>) {
Kobalt.INJECTOR = Guice.createInjector(MainModule(Args(), KobaltSettings.readSettingsXml()))
KobaltClient().run()
KobaltWebSocketClient().run()
}
interface Api {
@ -39,6 +48,53 @@ interface Api {
fun getDependencies(@Query("buildFile") buildFile: String) : Call<List<DependencyData.GetDependenciesData>>
}
class KobaltWebSocketClient : Runnable {
override fun run() {
val client = OkHttpClient()
val request = Request.Builder()
// .url("ws://echo.websocket.org")
.url("ws://localhost:1234/v1/getDependencies?buildFile=/Users/beust/kotlin/kobalt/kobalt/src/Build.kt")
.build()
var webSocket: WebSocket? = null
val ws = WebSocketCall.create(client, request).enqueue(object: WebSocketListener {
override fun onOpen(ws: WebSocket, response: Response) {
webSocket = ws
}
override fun onPong(p0: Buffer?) {
println("WebSocket pong")
}
override fun onClose(p0: Int, p1: String?) {
println("WebSocket closed")
}
override fun onFailure(ex: IOException, response: Response?) {
ex.printStackTrace()
error("WebSocket failure: ${ex.message} response: $response")
}
override fun onMessage(body: ResponseBody) {
val json = body.string()
val wsCommand = Gson().fromJson(json, WebSocketCommand::class.java)
if (wsCommand.errorMessage != null) {
warn("Received error message from server: " + wsCommand.errorMessage)
} else {
if (wsCommand.commandName == DependencyData.GetDependenciesData.NAME) {
val dd = Gson().fromJson(wsCommand.payload, DependencyData.GetDependenciesData::class.java)
println("Received dependency data: " + dd.projects.size + " projects")
} else if (wsCommand.commandName == ProgressCommand.NAME) {
val progress = Gson().fromJson(wsCommand.payload, ProgressCommand::class.java)
println(progress.message + (progress.progress ?: ""))
} else {
throw KobaltException("Unknown command: ${wsCommand.commandName} json:\n$json")
}
}
}
})
}
}
class KobaltClient : Runnable {
var outgoing: PrintWriter? = null

View file

@ -8,6 +8,9 @@ import com.beust.kobalt.app.Templates
import com.beust.kobalt.internal.PluginInfo
import com.google.common.collect.ListMultimap
import com.google.gson.Gson
import org.eclipse.jetty.websocket.api.RemoteEndpoint
import org.eclipse.jetty.websocket.api.Session
import org.eclipse.jetty.websocket.api.WebSocketListener
import spark.ResponseTransformer
import spark.Route
import spark.Spark
@ -34,8 +37,12 @@ class SparkServer(val initCallback: (String) -> List<Project>, val cleanUpCallba
private fun jsonRoute(path: String, route: Route)
= Spark.get(path, "application/json", route, JsonTransformer())
val log = org.slf4j.LoggerFactory.getLogger("SparkServer")
override fun run(port: Int) {
log.debug("RUNNING")
Spark.port(port)
Spark.webSocket("/v1/getDependencies", GetDependenciesChatHandler::class.java)
Spark.get("/ping", { req, res -> """ { "result" : "ok" } """ })
Spark.get("/quit", { req, res ->
Executors.newFixedThreadPool(1).let { executor ->
@ -72,9 +79,76 @@ class SparkServer(val initCallback: (String) -> List<Project>, val cleanUpCallba
jsonRoute("/v0/getTemplates", Route { request, response ->
TemplatesData.create(Templates().getTemplates(pluginInfo))
})
Spark.init()
}
}
class GetDependenciesChatHandler : WebSocketListener {
var session: Session? = null
override fun onWebSocketClose(code: Int, reason: String?) {
println("ON CLOSE $code reason: $reason")
}
override fun onWebSocketError(cause: Throwable?) {
cause?.printStackTrace()
throw UnsupportedOperationException()
}
fun <T> sendWebsocketCommand(endpoint: RemoteEndpoint, commandName: String, payload: T) {
endpoint.sendString(Gson().toJson(WebSocketCommand(commandName, payload = Gson().toJson(payload))))
}
override fun onWebSocketConnect(s: Session) {
session = s
val buildFileParams = s.upgradeRequest.parameterMap["buildFile"]
if (buildFileParams != null) {
val buildFile = buildFileParams[0]
val result = if (buildFile != null) {
try {
val dependencyData = Kobalt.INJECTOR.getInstance(DependencyData::class.java)
val args = Kobalt.INJECTOR.getInstance(Args::class.java)
dependencyData.dependenciesDataFor(buildFile, args, object : IProgressListener {
override fun onProgress(progress: Int?, message: String?) {
sendWebsocketCommand(s.remote, ProgressCommand.NAME, ProgressCommand(progress, message))
}
})
} catch(ex: Exception) {
DependencyData.GetDependenciesData(errorMessage = ex.message)
} finally {
SparkServer.cleanUpCallback()
}
} else {
DependencyData.GetDependenciesData(
errorMessage = "buildFile wasn't passed in the query parameter")
}
println("GOT DEPENDENCY DATA: $result")
sendWebsocketCommand(s.remote, DependencyData.GetDependenciesData.NAME, result)
s.close()
}
}
override fun onWebSocketText(message: String?) {
println("RECEIVED TEXT: $message")
session?.remote?.sendString("Response: $message")
}
override fun onWebSocketBinary(payload: ByteArray?, offset: Int, len: Int) {
println("RECEIVED BINARY: $payload")
}
}
class ProgressCommand(val progress: Int? = null, val message: String? = null) {
companion object {
val NAME = "ProgressCommand"
}
}
class WebSocketCommand(val commandName: String, val errorMessage: String? = null, val payload: String)
class TemplateData(val pluginName: String, val templates: List<String>)
class TemplatesData(val templates: List<TemplateData>) {