DemoBench: open web page automatically once web server has started.

This commit is contained in:
Mike Hearn
2017-04-24 12:59:22 +02:00
parent e139df7891
commit 2de5c0b218
2 changed files with 59 additions and 35 deletions

View File

@ -1,6 +1,5 @@
package net.corda.demobench.views package net.corda.demobench.views
import com.google.common.util.concurrent.RateLimiter
import com.jediterm.terminal.TerminalColor import com.jediterm.terminal.TerminalColor
import com.jediterm.terminal.TextStyle import com.jediterm.terminal.TextStyle
import com.jediterm.terminal.ui.settings.DefaultSettingsProvider import com.jediterm.terminal.ui.settings.DefaultSettingsProvider
@ -13,6 +12,8 @@ import javafx.scene.layout.StackPane
import javafx.scene.layout.VBox import javafx.scene.layout.VBox
import javafx.util.Duration import javafx.util.Duration
import net.corda.client.rpc.notUsed import net.corda.client.rpc.notUsed
import net.corda.core.success
import net.corda.core.then
import net.corda.demobench.explorer.ExplorerController import net.corda.demobench.explorer.ExplorerController
import net.corda.demobench.model.NodeConfig import net.corda.demobench.model.NodeConfig
import net.corda.demobench.model.NodeController import net.corda.demobench.model.NodeController
@ -24,12 +25,9 @@ import net.corda.demobench.web.DBViewer
import net.corda.demobench.web.WebServerController import net.corda.demobench.web.WebServerController
import tornadofx.* import tornadofx.*
import java.awt.Dimension import java.awt.Dimension
import java.io.IOException import java.net.URI
import java.net.SocketException
import java.net.URL
import java.util.logging.Level import java.util.logging.Level
import javax.swing.SwingUtilities import javax.swing.SwingUtilities
import kotlin.concurrent.thread
class NodeTerminalView : Fragment() { class NodeTerminalView : Fragment() {
override val root by fxml<VBox>() override val root by fxml<VBox>()
@ -131,6 +129,8 @@ class NodeTerminalView : Fragment() {
} }
} }
private var webURL: URI? = null
/* /*
* We only want to run one web server for each node. * We only want to run one web server for each node.
* So disable the "launch" button when we have * So disable the "launch" button when we have
@ -139,32 +139,20 @@ class NodeTerminalView : Fragment() {
*/ */
fun configureWebButton(config: NodeConfig) { fun configureWebButton(config: NodeConfig) {
launchWebButton.setOnAction { launchWebButton.setOnAction {
if (webURL != null) {
app.hostServices.showDocument(webURL.toString())
return@setOnAction
}
launchWebButton.isDisable = true launchWebButton.isDisable = true
webServer.open(config, onExit = { log.info("Starting web server for ${config.legalName}")
launchWebButton.isDisable = false webServer.open(config) then {
}) Platform.runLater { launchWebButton.isDisable = false }
} success {
openBrowserWhenWebServerHasStarted(config) log.info("Web server for ${config.legalName} started on $it")
} Platform.runLater {
} webURL = it
app.hostServices.showDocument(it.toString())
private fun openBrowserWhenWebServerHasStarted(config: NodeConfig) {
thread {
log.info("Waiting for web server to start ...")
val url = URL("http://localhost:${config.webPort}/")
val rateLimiter = RateLimiter.create(1.0)
while (true) {
try {
rateLimiter.acquire()
val conn = url.openConnection()
conn.connectTimeout = 1000 // msec
conn.connect()
log.info("Web server started")
app.hostServices.showDocument(url.toString())
break
} catch(e: SocketException) {
} catch(e: IOException) {
} }
} }
} }

View File

@ -1,10 +1,21 @@
package net.corda.demobench.web package net.corda.demobench.web
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.RateLimiter
import com.google.common.util.concurrent.SettableFuture
import net.corda.core.catch
import net.corda.core.minutes
import net.corda.core.until
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.demobench.model.NodeConfig import net.corda.demobench.model.NodeConfig
import net.corda.demobench.readErrorLines import net.corda.demobench.readErrorLines
import java.io.IOException import java.io.IOException
import java.net.HttpURLConnection
import java.net.URI
import java.time.Instant
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeoutException
import kotlin.concurrent.thread
class WebServer internal constructor(private val webServerController: WebServerController) : AutoCloseable { class WebServer internal constructor(private val webServerController: WebServerController) : AutoCloseable {
private companion object { private companion object {
@ -15,13 +26,12 @@ class WebServer internal constructor(private val webServerController: WebServerC
private var process: Process? = null private var process: Process? = null
@Throws(IOException::class) @Throws(IOException::class)
fun open(config: NodeConfig, onExit: (NodeConfig) -> Unit) { fun open(config: NodeConfig): ListenableFuture<URI> {
val nodeDir = config.nodeDir.toFile() val nodeDir = config.nodeDir.toFile()
if (!nodeDir.isDirectory) { if (!nodeDir.isDirectory) {
log.warn("Working directory '{}' does not exist.", nodeDir.absolutePath) log.warn("Working directory '{}' does not exist.", nodeDir.absolutePath)
onExit(config) return SettableFuture.create()
return
} }
try { try {
@ -46,12 +56,18 @@ class WebServer internal constructor(private val webServerController: WebServerC
} else { } else {
log.error("Web Server for '{}' has exited (value={}, {})", config.legalName, exitValue, errors) log.error("Web Server for '{}' has exited (value={}, {})", config.legalName, exitValue, errors)
} }
onExit(config)
} }
val future = SettableFuture.create<URI>()
thread {
future.catch {
log.info("Waiting for web server for ${config.legalName} to start ...")
waitForStart(config.webPort)
}
}
return future
} catch (e: IOException) { } catch (e: IOException) {
log.error("Failed to launch Web Server for '{}': {}", config.legalName, e.message) log.error("Failed to launch Web Server for '{}': {}", config.legalName, e.message)
onExit(config)
throw e throw e
} }
} }
@ -68,4 +84,24 @@ class WebServer internal constructor(private val webServerController: WebServerC
log.error("Failed to close stream: '{}'", e.message) log.error("Failed to close stream: '{}'", e.message)
} }
} }
private fun waitForStart(port: Int): URI {
val url = URI("http://localhost:$port/")
val rateLimiter = RateLimiter.create(2.0)
val start = Instant.now()
val timeout = 1.minutes
while ((start until Instant.now()) < timeout) {
try {
rateLimiter.acquire()
val conn = url.toURL().openConnection() as HttpURLConnection
conn.connectTimeout = 500 // msec
conn.requestMethod = "HEAD"
conn.connect()
conn.disconnect()
return url
} catch(e: IOException) {
}
}
throw TimeoutException("Web server did not start within ${timeout.seconds} seconds")
}
} }