mirror of
https://github.com/ethauvin/kobalt.git
synced 2025-04-26 08:27:12 -07:00
Make Kobalt reentrant.
This commit is contained in:
parent
fdcca3a1aa
commit
bace815481
7 changed files with 228 additions and 36 deletions
|
@ -26,6 +26,10 @@ class Args {
|
|||
"actually running them")
|
||||
var dryRun: Boolean = false
|
||||
|
||||
@Parameter(names = arrayOf("--force"), description = "Force a new server to be launched even if another one" +
|
||||
" is already running")
|
||||
var force: Boolean = false
|
||||
|
||||
@Parameter(names = arrayOf("--gc"), description = "Delete old files")
|
||||
var gc: Boolean = false
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ import com.beust.kobalt.Plugins
|
|||
import com.beust.kobalt.ProxyConfig
|
||||
import com.google.inject.Injector
|
||||
import org.eclipse.aether.repository.Proxy
|
||||
import com.beust.kobalt.internal.PluginInfo
|
||||
import com.google.inject.Guice
|
||||
import com.google.inject.Module
|
||||
import java.io.InputStream
|
||||
import java.net.InetSocketAddress
|
||||
import java.time.Duration
|
||||
|
@ -15,6 +18,18 @@ class Kobalt {
|
|||
companion object {
|
||||
lateinit var INJECTOR : Injector
|
||||
|
||||
fun init(module: Module) {
|
||||
Kobalt.INJECTOR = Guice.createInjector(module)
|
||||
|
||||
//
|
||||
// Add all the plugins read in kobalt-plugin.xml to the Plugins singleton, so that code
|
||||
// in the build file that calls Plugins.findPlugin() can find them (code in the
|
||||
// build file do not have access to the KobaltContext).
|
||||
//
|
||||
val pluginInfo = Kobalt.INJECTOR.getInstance(PluginInfo::class.java)
|
||||
pluginInfo.plugins.forEach { Plugins.addPluginInstance(it) }
|
||||
}
|
||||
|
||||
var context: KobaltContext? = null
|
||||
|
||||
val proxyConfig = with(Kobalt.context?.settings?.proxy) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.beust.kobalt.api.Kobalt
|
|||
import com.beust.kobalt.api.PluginTask
|
||||
import com.beust.kobalt.api.Project
|
||||
import com.beust.kobalt.app.*
|
||||
import com.beust.kobalt.app.remote.DependencyData
|
||||
import com.beust.kobalt.app.remote.KobaltClient
|
||||
import com.beust.kobalt.app.remote.KobaltServer
|
||||
import com.beust.kobalt.internal.Gc
|
||||
|
@ -18,7 +19,6 @@ import com.beust.kobalt.maven.Http
|
|||
import com.beust.kobalt.maven.dependency.FileDependency
|
||||
import com.beust.kobalt.misc.*
|
||||
import com.google.common.collect.HashMultimap
|
||||
import com.google.inject.Guice
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.Paths
|
||||
|
@ -42,7 +42,7 @@ private fun parseArgs(argv: Array<String>): Main.RunInfo {
|
|||
|
||||
fun mainNoExit(argv: Array<String>): Int {
|
||||
val (jc, args) = parseArgs(argv)
|
||||
Kobalt.INJECTOR = Guice.createInjector(MainModule(args, KobaltSettings.readSettingsXml()))
|
||||
Kobalt.init(MainModule(args, KobaltSettings.readSettingsXml()))
|
||||
val result = Kobalt.INJECTOR.getInstance(Main::class.java).run {
|
||||
val runResult = run(jc, args, argv)
|
||||
pluginInfo.shutdown()
|
||||
|
@ -64,8 +64,8 @@ private class Main @Inject constructor(
|
|||
val github: GithubApi2,
|
||||
val updateKobalt: UpdateKobalt,
|
||||
val client: KobaltClient,
|
||||
val server: KobaltServer,
|
||||
val pluginInfo: PluginInfo,
|
||||
val dependencyData: DependencyData,
|
||||
val projectGenerator: ProjectGenerator,
|
||||
val resolveDependency: ResolveDependency) {
|
||||
|
||||
|
@ -91,21 +91,12 @@ private class Main @Inject constructor(
|
|||
}
|
||||
|
||||
fun run(jc: JCommander, args: Args, argv: Array<String>): Int {
|
||||
|
||||
//
|
||||
// Install plug-ins requested from the command line
|
||||
//
|
||||
val pluginClassLoader = installCommandLinePlugins(args)
|
||||
|
||||
//
|
||||
// Add all the plugins read in kobalt-plugin.xml to the Plugins singleton, so that code
|
||||
// in the build file that calls Plugins.findPlugin() can find them (code in the
|
||||
// build file do not have access to the KobaltContext).
|
||||
//
|
||||
pluginInfo.plugins.forEach { Plugins.addPluginInstance(it) }
|
||||
|
||||
// val data = dependencyData.dependenciesDataFor(homeDir("kotlin/klaxon/kobalt/src/Build.kt"), Args())
|
||||
// println("Data: $data")
|
||||
|
||||
// --listTemplates
|
||||
if (args.listTemplates) {
|
||||
Templates().list(pluginInfo)
|
||||
|
@ -166,7 +157,7 @@ private class Main @Inject constructor(
|
|||
} else if (args.usage) {
|
||||
jc.usage()
|
||||
} else if (args.serverMode) {
|
||||
server.run()
|
||||
val port = KobaltServer(args.force, { pluginInfo.shutdown()}).call()
|
||||
} else {
|
||||
// Options that don't need Build.kt to be parsed first
|
||||
if (args.gc) {
|
||||
|
@ -213,6 +204,9 @@ private class Main @Inject constructor(
|
|||
//
|
||||
plugins.applyPlugins(Kobalt.context!!, allProjects)
|
||||
|
||||
// DONOTCOMMIT
|
||||
// val data = dependencyData.dependenciesDataFor(homeDir("kotlin/klaxon/kobalt/src/Build.kt"), Args())
|
||||
// println("Data: $data")
|
||||
|
||||
if (args.projectInfo) {
|
||||
// --projectInfo
|
||||
|
|
|
@ -1,18 +1,128 @@
|
|||
package com.beust.kobalt.app.remote
|
||||
|
||||
import com.beust.kobalt.Args
|
||||
import com.beust.kobalt.SystemProperties
|
||||
import com.beust.kobalt.api.Kobalt
|
||||
import com.beust.kobalt.app.MainModule
|
||||
import com.beust.kobalt.homeDir
|
||||
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 java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.io.PrintWriter
|
||||
import com.google.inject.Guice
|
||||
import java.io.*
|
||||
import java.net.ConnectException
|
||||
import java.net.Socket
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
import javax.inject.Inject
|
||||
|
||||
fun main(argv: Array<String>) {
|
||||
Kobalt.INJECTOR = Guice.createInjector(MainModule(Args(), KobaltSettings.readSettingsXml()))
|
||||
val port = ServerProcess().launch()
|
||||
println("SERVER RUNNING ON PORT $port")
|
||||
}
|
||||
|
||||
class ServerProcess {
|
||||
val SERVER_FILE = KFiles.joinDir(homeDir(KFiles.KOBALT_DOT_DIR, "kobaltServer.properties"))
|
||||
val KEY_PORT = "port"
|
||||
val executor = Executors.newFixedThreadPool(5)
|
||||
|
||||
fun launch() : Int {
|
||||
var port = launchPrivate()
|
||||
while (port == 0) {
|
||||
executor.submit {
|
||||
KobaltServer(force = true, shutdownCallback = {}).call()
|
||||
}
|
||||
// launchServer(ProcessUtil.findAvailablePort())
|
||||
port = launchPrivate()
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
private fun launchPrivate() : Int {
|
||||
var result = 0
|
||||
File(SERVER_FILE).let { serverFile ->
|
||||
if (serverFile.exists()) {
|
||||
val properties = Properties().apply {
|
||||
load(FileReader(serverFile))
|
||||
}
|
||||
|
||||
try {
|
||||
val socket = Socket("localhost", result)
|
||||
val outgoing = PrintWriter(socket.outputStream, true)
|
||||
val c: String = """{ "name": "ping"}"""
|
||||
outgoing.println(c)
|
||||
val ins = BufferedReader(InputStreamReader(socket.inputStream))
|
||||
var line = ins.readLine()
|
||||
val jo = JsonParser().parse(line) as JsonObject
|
||||
val jsonData = jo["data"]?.asString
|
||||
val dataObject = JsonParser().parse(jsonData) as JsonObject
|
||||
val received = JsonParser().parse(dataObject["received"].asString) as JsonObject
|
||||
if (received["name"].asString == "ping") {
|
||||
result = properties.getProperty(KEY_PORT).toInt()
|
||||
}
|
||||
} catch(ex: IOException) {
|
||||
log(1, "Couldn't connect to current server, launching a new one")
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun launchServer(port: Int) {
|
||||
val kobaltJar = File(KFiles().kobaltJar[0])
|
||||
log(1, "Kobalt jar: $kobaltJar")
|
||||
if (! kobaltJar.exists()) {
|
||||
warn("Can't find the jar file " + kobaltJar.absolutePath + " can't be found")
|
||||
} else {
|
||||
val args = listOf("java",
|
||||
"-classpath", KFiles().kobaltJar.joinToString(File.pathSeparator),
|
||||
"com.beust.kobalt.MainKt",
|
||||
"--dev", "--server", "--port", port.toString())
|
||||
val pb = ProcessBuilder(args)
|
||||
// pb.directory(File(directory))
|
||||
pb.inheritIO()
|
||||
// pb.environment().put("JAVA_HOME", ProjectJdkTable.getInstance().allJdks[0].homePath)
|
||||
val tempFile = createTempFile("kobalt")
|
||||
pb.redirectOutput(tempFile)
|
||||
warn("Launching " + args.joinToString(" "))
|
||||
warn("Server output in: $tempFile")
|
||||
val process = pb.start()
|
||||
val errorCode = process.waitFor()
|
||||
if (errorCode == 0) {
|
||||
log(1, "Server exiting")
|
||||
} else {
|
||||
log(1, "Server exiting with error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createServerFile(port: Int, force: Boolean) : Boolean {
|
||||
if (File(SERVER_FILE).exists() && ! force) {
|
||||
log(1, "Server file $SERVER_FILE already exists, is another server running?")
|
||||
return false
|
||||
} else {
|
||||
Properties().apply {
|
||||
put(KEY_PORT, port.toString())
|
||||
}.store(FileWriter(SERVER_FILE), "")
|
||||
log(2, "KobaltServer created $SERVER_FILE")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteServerFile() {
|
||||
log(1, "KobaltServer deleting $SERVER_FILE")
|
||||
File(SERVER_FILE).delete()
|
||||
}
|
||||
}
|
||||
|
||||
class KobaltClient @Inject constructor() : Runnable {
|
||||
var outgoing: PrintWriter? = null
|
||||
|
||||
|
|
39
src/main/kotlin/com/beust/kobalt/app/remote/KobaltHub.kt
Normal file
39
src/main/kotlin/com/beust/kobalt/app/remote/KobaltHub.kt
Normal file
|
@ -0,0 +1,39 @@
|
|||
package com.beust.kobalt.app.remote
|
||||
|
||||
import com.beust.kobalt.Args
|
||||
import com.beust.kobalt.api.Kobalt
|
||||
import com.beust.kobalt.app.MainModule
|
||||
import com.beust.kobalt.internal.KobaltSettings
|
||||
import com.beust.kobalt.internal.remote.ICommand
|
||||
import com.google.gson.Gson
|
||||
|
||||
enum class Command(val n: Int, val command: ICommand) {
|
||||
GET_DEPENDENCIES(1, Kobalt.INJECTOR.getInstance(GetDependenciesCommand::class.java));
|
||||
companion object {
|
||||
val commandMap = hashMapOf<Int, ICommand>()
|
||||
fun commandFor(n: Int) = values().filter { it.n == n }[0].command
|
||||
}
|
||||
}
|
||||
|
||||
class KobaltHub(val dependencyData: DependencyData) {
|
||||
val args = Args()
|
||||
|
||||
fun runCommand(n: Int) : String {
|
||||
val data =
|
||||
when(n) {
|
||||
1 -> Gson().toJson(
|
||||
dependencyData.dependenciesDataFor("/Users/beust/kotlin/klaxon/kobalt/src/Build.kt", args))
|
||||
else -> throw RuntimeException("Unknown command")
|
||||
}
|
||||
println("Data: $data")
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
fun main(argv: Array<String>) {
|
||||
Kobalt.init(MainModule(Args(), KobaltSettings.readSettingsXml()))
|
||||
val dependencyData = Kobalt.INJECTOR.getInstance(DependencyData::class.java)
|
||||
val json = KobaltHub(dependencyData).runCommand(1)
|
||||
val dd = Gson().fromJson(json, DependencyData.GetDependenciesData::class.java)
|
||||
println("Data2: $dd")
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
package com.beust.kobalt.app.remote
|
||||
|
||||
import com.beust.kobalt.Args
|
||||
import com.beust.kobalt.api.Kobalt
|
||||
import com.beust.kobalt.homeDir
|
||||
import com.beust.kobalt.internal.PluginInfo
|
||||
import com.beust.kobalt.internal.remote.CommandData
|
||||
import com.beust.kobalt.internal.remote.ICommandSender
|
||||
import com.beust.kobalt.internal.remote.PingCommand
|
||||
|
@ -12,15 +10,14 @@ import com.beust.kobalt.misc.log
|
|||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import com.google.inject.Singleton
|
||||
import java.io.*
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.net.ServerSocket
|
||||
import java.net.SocketException
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
@Singleton
|
||||
class KobaltServer @Inject constructor(val args: Args, val pluginInfo: PluginInfo) : Runnable, ICommandSender {
|
||||
class KobaltServer(val force: Boolean, val shutdownCallback: () -> Unit) : Callable<Int>, ICommandSender {
|
||||
// var outgoing: PrintWriter? = null
|
||||
val pending = arrayListOf<CommandData>()
|
||||
|
||||
|
@ -29,28 +26,34 @@ class KobaltServer @Inject constructor(val args: Args, val pluginInfo: PluginInf
|
|||
Kobalt.INJECTOR.getInstance(it).let { Pair(it.name, it) }
|
||||
}.toMap()
|
||||
|
||||
override fun run() {
|
||||
override fun call() : Int {
|
||||
val port = ProcessUtil.findAvailablePort()
|
||||
try {
|
||||
if (createServerFile(args.port)) {
|
||||
privateRun()
|
||||
if (createServerFile(port, force)) {
|
||||
privateRun(port)
|
||||
}
|
||||
} catch(ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
} finally {
|
||||
deleteServerFile()
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
val SERVER_FILE = KFiles.joinDir(homeDir(KFiles.KOBALT_DOT_DIR, "kobaltServer.properties"))
|
||||
val KEY_PORT = "port"
|
||||
val KEY_PID = "pid"
|
||||
|
||||
private fun createServerFile(port: Int) : Boolean {
|
||||
if (File(SERVER_FILE).exists()) {
|
||||
private fun createServerFile(port: Int, force: Boolean) : Boolean {
|
||||
if (File(SERVER_FILE).exists() && ! force) {
|
||||
log(1, "Server file $SERVER_FILE already exists, is another server running?")
|
||||
return false
|
||||
} else {
|
||||
val processName = ManagementFactory.getRuntimeMXBean().name
|
||||
val pid = processName.split("@")[0]
|
||||
Properties().apply {
|
||||
put(KEY_PORT, port.toString())
|
||||
put(KEY_PID, pid)
|
||||
}.store(FileWriter(SERVER_FILE), "")
|
||||
log(2, "KobaltServer created $SERVER_FILE")
|
||||
return true
|
||||
|
@ -84,12 +87,10 @@ class KobaltServer @Inject constructor(val args: Args, val pluginInfo: PluginInf
|
|||
}
|
||||
}
|
||||
|
||||
private fun privateRun() {
|
||||
val portNumber = args.port
|
||||
|
||||
log(1, "Listening to port $portNumber")
|
||||
private fun privateRun(port: Int) {
|
||||
log(1, "Listening to port $port")
|
||||
var quit = false
|
||||
serverInfo = ServerInfo(portNumber)
|
||||
serverInfo = ServerInfo(port)
|
||||
while (!quit) {
|
||||
if (pending.size > 0) {
|
||||
log(1, "Emptying the queue, size $pending.size()")
|
||||
|
@ -114,10 +115,13 @@ class KobaltServer @Inject constructor(val args: Args, val pluginInfo: PluginInf
|
|||
// Done, send a quit to the client
|
||||
sendData(CommandData("quit", ""))
|
||||
|
||||
// Clean up all the plug-in actors
|
||||
shutdownCallback()
|
||||
line = serverInfo.reader.readLine()
|
||||
}
|
||||
}
|
||||
if (line == null) {
|
||||
log(1, "Received null line, resetting the server")
|
||||
serverInfo.reset()
|
||||
}
|
||||
} catch(ex: SocketException) {
|
||||
|
@ -130,8 +134,6 @@ class KobaltServer @Inject constructor(val args: Args, val pluginInfo: PluginInf
|
|||
}
|
||||
log(1, "Command failed: ${ex.message}")
|
||||
}
|
||||
|
||||
pluginInfo.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,4 +157,5 @@ class KobaltServer @Inject constructor(val args: Args, val pluginInfo: PluginInf
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
27
src/main/kotlin/com/beust/kobalt/app/remote/ProcessUtil.kt
Normal file
27
src/main/kotlin/com/beust/kobalt/app/remote/ProcessUtil.kt
Normal file
|
@ -0,0 +1,27 @@
|
|||
package com.beust.kobalt.app.remote
|
||||
|
||||
import java.io.IOException
|
||||
import java.net.Socket
|
||||
|
||||
class ProcessUtil {
|
||||
companion object {
|
||||
fun findAvailablePort(): Int {
|
||||
for (i in 1234..65000) {
|
||||
if (isPortAvailable(i)) return i
|
||||
}
|
||||
throw IllegalArgumentException("Couldn't find any port available, something is very wrong")
|
||||
}
|
||||
|
||||
private fun isPortAvailable(port: Int): Boolean {
|
||||
var s: Socket? = null
|
||||
try {
|
||||
s = Socket("localhost", port)
|
||||
return false
|
||||
} catch(ex: IOException) {
|
||||
return true
|
||||
} finally {
|
||||
s?.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue